From 238f5bb3389036e919e427510f8fc8f1825c13e5 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Thu, 17 Aug 2017 16:25:53 +0200 Subject: [PATCH] Add support for 1.12.1 (#3908) --- README.md | 18 +- src/Protocol/ProtocolRecognizer.cpp | 6 + src/Protocol/ProtocolRecognizer.h | 7 +- src/Protocol/Protocol_1_12.cpp | 759 +++++++++++++++++++++++++++- src/Protocol/Protocol_1_12.h | 59 +++ 5 files changed, 836 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 463dd5284..57987fd8e 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Cuberite is a Minecraft-compatible multiplayer game server that is written in C+ Cuberite can run on Windows, *nix and Android operating systems. This includes Android phones and tablets as well as Raspberry Pis. -We currently support Release 1.8 - 1.12 Minecraft protocol versions. +We currently support Release 1.8 - 1.12.1 Minecraft protocol versions. Subscribe to [the newsletter](https://cuberite.org/news/#subscribe) for important updates and project news. @@ -15,18 +15,21 @@ Installation There are several ways to obtain Cuberite. #### Binaries - - The easiest method is downloading for Windows or Linux from the [Project site](https://cuberite.org/). - - You can use the EasyInstall script for Linux and macOS, which automatically downloads the correct binary. The script is described below. - - You can also obtain a binary from the [buildserver archive](https://builds.cuberite.org/). + +- The easiest method is downloading for Windows or Linux from the [Project site](https://cuberite.org/). +- You can use the EasyInstall script for Linux and macOS, which automatically downloads the correct binary. The script is described below. +- You can also obtain a binary from the [buildserver archive](https://builds.cuberite.org/). ##### The EasyInstall script + This script will download the correct binary from the project site. curl -sSfL https://download.cuberite.org | sh #### Compiling - - You can compile automatically for Linux / *nix with the `compile.sh` script. The script is described below. - - You can also compile manually. See [COMPILING.md](https://github.com/cuberite/cuberite/blob/master/COMPILING.md). + +- You can compile automatically for Linux / *nix with the `compile.sh` script. The script is described below. +- You can also compile manually. See [COMPILING.md](https://github.com/cuberite/cuberite/blob/master/COMPILING.md). Compiling may provide better performance (1.5-3x as fast) and it supports more operating systems. @@ -36,7 +39,8 @@ This script downloads the source code and compiles it. The script is smart enoug sh -c "$(wget -O - https://compile.cuberite.org)" #### Hosted services - - Hosted Cuberite is available via [Gamocosm](https://gamocosm.com/). + +- Hosted Cuberite is available via [Gamocosm](https://gamocosm.com/). Contributing ------------ diff --git a/src/Protocol/ProtocolRecognizer.cpp b/src/Protocol/ProtocolRecognizer.cpp index 43facde72..a65fb931e 100644 --- a/src/Protocol/ProtocolRecognizer.cpp +++ b/src/Protocol/ProtocolRecognizer.cpp @@ -59,6 +59,7 @@ AString cProtocolRecognizer::GetVersionTextFromInt(int a_ProtocolVersion) case PROTO_VERSION_1_11_0: return "1.11"; case PROTO_VERSION_1_11_1: return "1.11.1"; case PROTO_VERSION_1_12: return "1.12"; + case PROTO_VERSION_1_12_1: return "1.12.1"; } ASSERT(!"Unknown protocol version"); return Printf("Unknown protocol (%d)", a_ProtocolVersion); @@ -1107,6 +1108,11 @@ bool cProtocolRecognizer::TryRecognizeLengthedProtocol(UInt32 a_PacketLengthRema m_Protocol = new cProtocol_1_12(m_Client, ServerAddress, ServerPort, NextState); return true; } + case PROTO_VERSION_1_12_1: + { + m_Protocol = new cProtocol_1_12_1(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 32dcca940..4c338a5b8 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" -#define MCS_PROTOCOL_VERSIONS "47, 107, 108, 109, 110, 210, 315, 316, 335" -#define MCS_LATEST_PROTOCOL_VERSION 335 +#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" +#define MCS_LATEST_PROTOCOL_VERSION 338 @@ -43,6 +43,7 @@ public: PROTO_VERSION_1_11_0 = 315, PROTO_VERSION_1_11_1 = 316, PROTO_VERSION_1_12 = 335, + PROTO_VERSION_1_12_1 = 338, } ; cProtocolRecognizer(cClientHandle * a_Client); diff --git a/src/Protocol/Protocol_1_12.cpp b/src/Protocol/Protocol_1_12.cpp index 278c063cc..b9eb1801d 100644 --- a/src/Protocol/Protocol_1_12.cpp +++ b/src/Protocol/Protocol_1_12.cpp @@ -1164,7 +1164,7 @@ void cProtocol_1_12::SendExperience(void) { ASSERT(m_State == 3); // In game mode? - cPacketizer Pkt(*this, 0x3f); // Experience Packet + cPacketizer Pkt(*this, 0x3f); // Set Experience Packet cPlayer * Player = m_Client->GetPlayer(); Pkt.WriteBEFloat(Player->GetXpPercentage()); Pkt.WriteVarInt32(static_cast(Player->GetXpLevel())); @@ -1211,7 +1211,7 @@ void cProtocol_1_12::SendScoreboardObjective(const AString & a_Name, const AStri void cProtocol_1_12::SendAttachEntity(const cEntity & a_Entity, const cEntity & a_Vehicle) { ASSERT(m_State == 3); // In game mode? - cPacketizer Pkt(*this, 0x42); // Set passangers packet + cPacketizer Pkt(*this, 0x42); // Set Passengers packet Pkt.WriteVarInt32(a_Vehicle.GetUniqueID()); Pkt.WriteVarInt32(1); // 1 passenger Pkt.WriteVarInt32(a_Entity.GetUniqueID()); @@ -1224,7 +1224,7 @@ void cProtocol_1_12::SendAttachEntity(const cEntity & a_Entity, const cEntity & void cProtocol_1_12::SendDetachEntity(const cEntity & a_Entity, const cEntity & a_PreviousVehicle) { ASSERT(m_State == 3); // In game mode? - cPacketizer Pkt(*this, 0x42); // Set passangers packet + cPacketizer Pkt(*this, 0x42); // Set Passengers packet Pkt.WriteVarInt32(a_PreviousVehicle.GetUniqueID()); Pkt.WriteVarInt32(0); // No passangers } @@ -1600,3 +1600,756 @@ bool cProtocol_1_12::HandlePacket(cByteBuffer & a_ByteBuffer, UInt32 a_PacketTyp m_Client->PacketUnknown(a_PacketType); return false; } + + + + + +cProtocol_1_12_1::cProtocol_1_12_1(cClientHandle * a_Client, const AString & a_ServerAddress, UInt16 a_ServerPort, UInt32 a_State) : + super(a_Client, a_ServerAddress, a_ServerPort, a_State) +{ +} + + + + + +void cProtocol_1_12_1::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.12.1"; + Version["protocol"] = cProtocolRecognizer::PROTO_VERSION_1_12_1; + + // 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; + 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, 0x00); // Response packet + Pkt.WriteString(Writer.write(ResponseValue)); +} + + + + + +void cProtocol_1_12_1::SendRespawn(eDimension a_Dimension) +{ + cPacketizer Pkt(*this, 0x35); // Respawn packet + cPlayer * Player = m_Client->GetPlayer(); + Pkt.WriteBEInt32(static_cast(a_Dimension)); + Pkt.WriteBEUInt8(2); // TODO: Difficulty (set to Normal) + Pkt.WriteBEUInt8(static_cast(Player->GetEffectiveGameMode())); + Pkt.WriteString("default"); +} + + + + + +void cProtocol_1_12_1::SendPlayerListAddPlayer(const cPlayer & a_Player) +{ + ASSERT(m_State == 3); // In game mode? + + cPacketizer Pkt(*this, 0x2e); // Player List Item packet + Pkt.WriteVarInt32(0); + Pkt.WriteVarInt32(1); + Pkt.WriteUUID(a_Player.GetUUID()); + Pkt.WriteString(a_Player.GetPlayerListName()); + + const Json::Value & Properties = a_Player.GetClientHandle()->GetProperties(); + Pkt.WriteVarInt32(Properties.size()); + for (auto & Node : Properties) + { + Pkt.WriteString(Node.get("name", "").asString()); + Pkt.WriteString(Node.get("value", "").asString()); + AString Signature = Node.get("signature", "").asString(); + if (Signature.empty()) + { + Pkt.WriteBool(false); + } + else + { + Pkt.WriteBool(true); + Pkt.WriteString(Signature); + } + } + + Pkt.WriteVarInt32(static_cast(a_Player.GetGameMode())); + Pkt.WriteVarInt32(static_cast(a_Player.GetClientHandle()->GetPing())); + Pkt.WriteBool(false); +} + + + + + +void cProtocol_1_12_1::SendPlayerListRemovePlayer(const cPlayer & a_Player) +{ + ASSERT(m_State == 3); // In game mode? + + cPacketizer Pkt(*this, 0x2e); // Player List Item packet + Pkt.WriteVarInt32(4); + Pkt.WriteVarInt32(1); + Pkt.WriteUUID(a_Player.GetUUID()); +} + + + + + +void cProtocol_1_12_1::SendPlayerListUpdateGameMode(const cPlayer & a_Player) +{ + ASSERT(m_State == 3); // In game mode? + + cPacketizer Pkt(*this, 0x2e); // Player List Item packet + Pkt.WriteVarInt32(1); + Pkt.WriteVarInt32(1); + Pkt.WriteUUID(a_Player.GetUUID()); + Pkt.WriteVarInt32(static_cast(a_Player.GetGameMode())); +} + + + + + +void cProtocol_1_12_1::SendPlayerListUpdatePing(const cPlayer & a_Player) +{ + ASSERT(m_State == 3); // In game mode? + + auto ClientHandle = a_Player.GetClientHandlePtr(); + if (ClientHandle != nullptr) + { + cPacketizer Pkt(*this, 0x2e); // Player List Item packet + Pkt.WriteVarInt32(2); + Pkt.WriteVarInt32(1); + Pkt.WriteUUID(a_Player.GetUUID()); + Pkt.WriteVarInt32(static_cast(ClientHandle->GetPing())); + } +} + + + + + +void cProtocol_1_12_1::SendPlayerListUpdateDisplayName(const cPlayer & a_Player, const AString & a_CustomName) +{ + ASSERT(m_State == 3); // In game mode? + + cPacketizer Pkt(*this, 0x2e); // Player List Item packet + Pkt.WriteVarInt32(3); + Pkt.WriteVarInt32(1); + Pkt.WriteUUID(a_Player.GetUUID()); + + if (a_CustomName.empty()) + { + Pkt.WriteBool(false); + } + else + { + Pkt.WriteBool(true); + Pkt.WriteString(Printf("{\"text\":\"%s\"}", a_CustomName.c_str())); + } +} + + + + + +void cProtocol_1_12_1::SendPlayerAbilities(void) +{ + ASSERT(m_State == 3); // In game mode? + + cPacketizer Pkt(*this, 0x2c); // Player Abilities packet + Byte Flags = 0; + cPlayer * Player = m_Client->GetPlayer(); + if (Player->IsGameModeCreative()) + { + Flags |= 0x01; + Flags |= 0x08; // Godmode, used for creative + } + if (Player->IsFlying()) + { + Flags |= 0x02; + } + if (Player->CanFly()) + { + Flags |= 0x04; + } + Pkt.WriteBEUInt8(Flags); + Pkt.WriteBEFloat(static_cast(0.05 * Player->GetFlyingMaxSpeed())); + Pkt.WriteBEFloat(static_cast(0.1 * Player->GetNormalMaxSpeed())); +} + + + + + +void cProtocol_1_12_1::SendPlayerMoveLook(void) +{ + ASSERT(m_State == 3); // In game mode? + + cPacketizer Pkt(*this, 0x2f); // Player Position And Look packet + cPlayer * Player = m_Client->GetPlayer(); + Pkt.WriteBEDouble(Player->GetPosX()); + Pkt.WriteBEDouble(Player->GetPosY()); + Pkt.WriteBEDouble(Player->GetPosZ()); + Pkt.WriteBEFloat(static_cast(Player->GetYaw())); + Pkt.WriteBEFloat(static_cast(Player->GetPitch())); + Pkt.WriteBEUInt8(0); + Pkt.WriteVarInt32(++m_OutstandingTeleportId); + + // This teleport ID hasn't been confirmed yet + m_IsTeleportIdConfirmed = false; +} + + + + + +void cProtocol_1_12_1::SendUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + ASSERT(m_State == 3); // In game mode? + + cPacketizer Pkt(*this, 0x30); // Use bed + Pkt.WriteVarInt32(a_Entity.GetUniqueID()); + Pkt.WritePosition64(a_BlockX, a_BlockY, a_BlockZ); +} + + + + + +void cProtocol_1_12_1::SendDestroyEntity(const cEntity & a_Entity) +{ + ASSERT(m_State == 3); // In game mode? + + cPacketizer Pkt(*this, 0x32); // Destroy Entities packet + Pkt.WriteVarInt32(1); + Pkt.WriteVarInt32(a_Entity.GetUniqueID()); +} + + + + + +void cProtocol_1_12_1::SendRemoveEntityEffect(const cEntity & a_Entity, int a_EffectID) +{ + ASSERT(m_State == 3); // In game mode? + + cPacketizer Pkt(*this, 0x33); // Remove entity effect packet + Pkt.WriteVarInt32(a_Entity.GetUniqueID()); + Pkt.WriteBEUInt8(static_cast(a_EffectID)); +} + + + + + +void cProtocol_1_12_1::SendEntityHeadLook(const cEntity & a_Entity) +{ + ASSERT(m_State == 3); // In game mode? + + cPacketizer Pkt(*this, 0x36); // Entity Head Look packet + Pkt.WriteVarInt32(a_Entity.GetUniqueID()); + Pkt.WriteByteAngle(a_Entity.GetHeadYaw()); +} + + + + + +void cProtocol_1_12_1::SendCameraSetTo(const cEntity & a_Entity) +{ + cPacketizer Pkt(*this, 0x39); // Camera packet (Attach the camera of a player at another entity in spectator mode) + Pkt.WriteVarInt32(a_Entity.GetUniqueID()); +} + + + + + +void cProtocol_1_12_1::SendDisplayObjective(const AString & a_Objective, cScoreboard::eDisplaySlot a_Display) +{ + ASSERT(m_State == 3); // In game mode? + + cPacketizer Pkt(*this, 0x3b); // Display Scoreboard packet + Pkt.WriteBEUInt8(static_cast(a_Display)); + Pkt.WriteString(a_Objective); +} + + + + + +void cProtocol_1_12_1::SendEntityMetadata(const cEntity & a_Entity) +{ + ASSERT(m_State == 3); // In game mode? + + cPacketizer Pkt(*this, 0x3c); // Entity Metadata packet + Pkt.WriteVarInt32(a_Entity.GetUniqueID()); + WriteEntityMetadata(Pkt, a_Entity); + Pkt.WriteBEUInt8(0xff); // The termination byte +} + + + + + +void cProtocol_1_12_1::SendEntityVelocity(const cEntity & a_Entity) +{ + ASSERT(m_State == 3); // In game mode? + + cPacketizer Pkt(*this, 0x3e); // Entity Velocity packet + Pkt.WriteVarInt32(a_Entity.GetUniqueID()); + // 400 = 8000 / 20 ... Conversion from our speed in m / s to 8000 m / tick + Pkt.WriteBEInt16(static_cast(a_Entity.GetSpeedX() * 400)); + Pkt.WriteBEInt16(static_cast(a_Entity.GetSpeedY() * 400)); + Pkt.WriteBEInt16(static_cast(a_Entity.GetSpeedZ() * 400)); +} + + + + + +void cProtocol_1_12_1::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) +{ + ASSERT(m_State == 3); // In game mode? + + cPacketizer Pkt(*this, 0x3f); // Entity Equipment packet + Pkt.WriteVarInt32(a_Entity.GetUniqueID()); + // Needs to be adjusted due to the insertion of offhand at slot 1 + if (a_SlotNum > 0) + { + a_SlotNum++; + } + Pkt.WriteVarInt32(static_cast(a_SlotNum)); + WriteItem(Pkt, a_Item); +} + + + + + +void cProtocol_1_12_1::SendExperience(void) +{ + ASSERT(m_State == 3); // In game mode? + + cPacketizer Pkt(*this, 0x40); // Set Experience packet + cPlayer * Player = m_Client->GetPlayer(); + Pkt.WriteBEFloat(Player->GetXpPercentage()); + Pkt.WriteVarInt32(static_cast(Player->GetXpLevel())); + Pkt.WriteVarInt32(static_cast(Player->GetCurrentXp())); +} + + + + + +void cProtocol_1_12_1::SendHealth(void) +{ + ASSERT(m_State == 3); // In game mode? + + cPacketizer Pkt(*this, 0x41); // Update Health packet + cPlayer * Player = m_Client->GetPlayer(); + Pkt.WriteBEFloat(static_cast(Player->GetHealth())); + Pkt.WriteVarInt32(static_cast(Player->GetFoodLevel())); + Pkt.WriteBEFloat(static_cast(Player->GetFoodSaturationLevel())); +} + + + + + +void cProtocol_1_12_1::SendScoreboardObjective(const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) +{ + ASSERT(m_State == 3); // In game mode? + + cPacketizer Pkt(*this, 0x42); // Scoreboard Objective packet + Pkt.WriteString(a_Name); + Pkt.WriteBEUInt8(a_Mode); + if ((a_Mode == 0) || (a_Mode == 2)) + { + Pkt.WriteString(a_DisplayName); + Pkt.WriteString("integer"); + } +} + + + + + +void cProtocol_1_12_1::SendAttachEntity(const cEntity & a_Entity, const cEntity & a_Vehicle) +{ + ASSERT(m_State == 3); // In game mode? + cPacketizer Pkt(*this, 0x43); // Set Passengers packet + Pkt.WriteVarInt32(a_Vehicle.GetUniqueID()); + Pkt.WriteVarInt32(1); // 1 passenger + Pkt.WriteVarInt32(a_Entity.GetUniqueID()); +} + + + + + +void cProtocol_1_12_1::SendDetachEntity(const cEntity & a_Entity, const cEntity & a_PreviousVehicle) +{ + ASSERT(m_State == 3); // In game mode? + cPacketizer Pkt(*this, 0x43); // Set Passengers packet + Pkt.WriteVarInt32(a_PreviousVehicle.GetUniqueID()); + Pkt.WriteVarInt32(0); // No passangers +} + + + + + +void cProtocol_1_12_1::SendScoreUpdate(const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode) +{ + ASSERT(m_State == 3); // In game mode? + + cPacketizer Pkt(*this, 0x45); // Update Score packet + Pkt.WriteString(a_Player); + Pkt.WriteBEUInt8(a_Mode); + Pkt.WriteString(a_Objective); + + if (a_Mode != 1) + { + Pkt.WriteVarInt32(static_cast(a_Score)); + } +} + + + + + +void cProtocol_1_12_1::SendLogin(const cPlayer & a_Player, const cWorld & a_World) +{ + // Send the Join Game packet: + { + cServer * Server = cRoot::Get()->GetServer(); + cPacketizer Pkt(*this, 0x23); // Join Game packet + Pkt.WriteBEUInt32(a_Player.GetUniqueID()); + Pkt.WriteBEUInt8(static_cast(a_Player.GetEffectiveGameMode()) | (Server->IsHardcore() ? 0x08 : 0)); // Hardcore flag bit 4 + Pkt.WriteBEInt32(static_cast(a_World.GetDimension())); + Pkt.WriteBEUInt8(2); // TODO: Difficulty (set to Normal) + Pkt.WriteBEUInt8(static_cast(Clamp(Server->GetMaxPlayers(), 0, 255))); + Pkt.WriteString("default"); // Level type - wtf? + Pkt.WriteBool(false); // Reduced Debug Info - wtf? + } + + // Send the spawn position: + { + cPacketizer Pkt(*this, 0x46); // Spawn Position packet + Pkt.WritePosition64(FloorC(a_World.GetSpawnX()), FloorC(a_World.GetSpawnY()), FloorC(a_World.GetSpawnZ())); + } + + // Send the server difficulty: + { + cPacketizer Pkt(*this, 0x0d); // Server difficulty packet + Pkt.WriteBEInt8(1); + } + + // Send player abilities: + SendPlayerAbilities(); +} + + + + + +void cProtocol_1_12_1::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay, bool a_DoDaylightCycle) +{ + ASSERT(m_State == 3); // In game mode? + if (!a_DoDaylightCycle) + { + // When writing a "-" before the number the client ignores it but it will stop the client-side time expiration. + a_TimeOfDay = std::min(-a_TimeOfDay, -1LL); + } + + cPacketizer Pkt(*this, 0x47); // Time update packet + Pkt.WriteBEInt64(a_WorldAge); + Pkt.WriteBEInt64(a_TimeOfDay); +} + + + + + +void cProtocol_1_12_1::SendSetRawTitle(const AString & a_Title) +{ + ASSERT(m_State == 3); // In game mode? + + cPacketizer Pkt(*this, 0x48); // Title packet + Pkt.WriteVarInt32(0); // Set title + + Pkt.WriteString(a_Title); +} + + + + + +void cProtocol_1_12_1::SendSetRawSubTitle(const AString & a_SubTitle) +{ + ASSERT(m_State == 3); // In game mode? + + cPacketizer Pkt(*this, 0x48); // Title packet + Pkt.WriteVarInt32(1); // Set subtitle + + Pkt.WriteString(a_SubTitle); +} + + + + + +void cProtocol_1_12_1::SendTitleTimes(int a_FadeInTicks, int a_DisplayTicks, int a_FadeOutTicks) +{ + ASSERT(m_State == 3); // In game mode? + + cPacketizer Pkt(*this, 0x48); // Title packet + Pkt.WriteVarInt32(3); // Set title display times + Pkt.WriteBEInt32(a_FadeInTicks); + Pkt.WriteBEInt32(a_DisplayTicks); + Pkt.WriteBEInt32(a_FadeOutTicks); +} + + + + + +void cProtocol_1_12_1::SendHideTitle(void) +{ + ASSERT(m_State == 3); // In game mode? + + cPacketizer Pkt(*this, 0x48); // Title packet + Pkt.WriteVarInt32(4); // Hide title +} + + + + + +void cProtocol_1_12_1::SendResetTitle(void) +{ + ASSERT(m_State == 3); // In game mode? + + cPacketizer Pkt(*this, 0x48); // Title packet + Pkt.WriteVarInt32(5); // Reset title +} + + + + + +void cProtocol_1_12_1::SendCollectEntity(const cEntity & a_Entity, const cPlayer & a_Player, int a_Count) +{ + ASSERT(m_State == 3); // In game mode? + + cPacketizer Pkt(*this, 0x4b); // Collect Item packet + Pkt.WriteVarInt32(a_Entity.GetUniqueID()); + Pkt.WriteVarInt32(a_Player.GetUniqueID()); + Pkt.WriteVarInt32(static_cast(a_Count)); +} + + + + + +void cProtocol_1_12_1::SendTeleportEntity(const cEntity & a_Entity) +{ + ASSERT(m_State == 3); // In game mode? + + cPacketizer Pkt(*this, 0x4c); // Entity teleport packet + Pkt.WriteVarInt32(a_Entity.GetUniqueID()); + Pkt.WriteBEDouble(a_Entity.GetPosX()); + Pkt.WriteBEDouble(a_Entity.GetPosY()); + Pkt.WriteBEDouble(a_Entity.GetPosZ()); + Pkt.WriteByteAngle(a_Entity.GetYaw()); + Pkt.WriteByteAngle(a_Entity.GetPitch()); + Pkt.WriteBool(a_Entity.IsOnGround()); +} + + + + + +void cProtocol_1_12_1::SendEntityProperties(const cEntity & a_Entity) +{ + ASSERT(m_State == 3); // In game mode? + + + cPacketizer Pkt(*this, 0x4e); // Entity Properties packet + Pkt.WriteVarInt32(a_Entity.GetUniqueID()); + WriteEntityProperties(Pkt, a_Entity); +} + + + + + +void cProtocol_1_12_1::SendEntityEffect(const cEntity & a_Entity, int a_EffectID, int a_Amplifier, short a_Duration) +{ + ASSERT(m_State == 3); // In game mode? + + cPacketizer Pkt(*this, 0x4f); // Entity Effect packet + Pkt.WriteVarInt32(a_Entity.GetUniqueID()); + Pkt.WriteBEUInt8(static_cast(a_EffectID)); + Pkt.WriteBEUInt8(static_cast(a_Amplifier)); + Pkt.WriteVarInt32(static_cast(a_Duration)); + Pkt.WriteBool(false); // Hide particles +} + + + + + +void cProtocol_1_12_1::SendPlayerMaxSpeed(void) +{ + ASSERT(m_State == 3); // In game mode? + + cPacketizer Pkt(*this, 0x4e); // Entity Properties + cPlayer * Player = m_Client->GetPlayer(); + Pkt.WriteVarInt32(Player->GetUniqueID()); + Pkt.WriteBEInt32(1); // Count + Pkt.WriteString("generic.movementSpeed"); + // The default game speed is 0.1, multiply that value by the relative speed: + Pkt.WriteBEDouble(0.1 * Player->GetNormalMaxSpeed()); + if (Player->IsSprinting()) + { + Pkt.WriteVarInt32(1); // Modifier count + Pkt.WriteBEUInt64(0x662a6b8dda3e4c1c); + Pkt.WriteBEUInt64(0x881396ea6097278d); // UUID of the modifier + Pkt.WriteBEDouble(Player->GetSprintingMaxSpeed() - Player->GetNormalMaxSpeed()); + Pkt.WriteBEUInt8(2); + } + else + { + Pkt.WriteVarInt32(0); // Modifier count + } +} + + + + + +bool cProtocol_1_12_1::HandlePacket(cByteBuffer & a_ByteBuffer, UInt32 a_PacketType) +{ + switch (m_State) + { + case 1: + { + // Status + switch (a_PacketType) + { + case 0x00: HandlePacketStatusRequest(a_ByteBuffer); return true; + case 0x01: HandlePacketStatusPing(a_ByteBuffer); return true; + } + break; + } + + case 2: + { + // Login + switch (a_PacketType) + { + case 0x00: HandlePacketLoginStart(a_ByteBuffer); return true; + case 0x01: HandlePacketLoginEncryptionResponse(a_ByteBuffer); return true; + } + break; + } + + case 3: + { + // Game + switch (a_PacketType) + { + case 0x00: HandleConfirmTeleport(a_ByteBuffer); return true; + case 0x01: 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 0x05: break; // Confirm transaction - not used in Cuberite + case 0x06: HandlePacketEnchantItem(a_ByteBuffer); return true; + case 0x07: HandlePacketWindowClick(a_ByteBuffer); return true; + case 0x08: HandlePacketWindowClose(a_ByteBuffer); return true; + case 0x09: HandlePacketPluginMessage(a_ByteBuffer); return true; + case 0x0a: HandlePacketUseEntity(a_ByteBuffer); return true; + case 0x0b: HandlePacketKeepAlive(a_ByteBuffer); return true; + case 0x0c: HandlePacketPlayer(a_ByteBuffer); return true; + case 0x0d: HandlePacketPlayerPos(a_ByteBuffer); return true; + case 0x0e: HandlePacketPlayerPosLook(a_ByteBuffer); return true; + case 0x0f: HandlePacketPlayerLook(a_ByteBuffer); return true; + case 0x10: HandlePacketVehicleMove(a_ByteBuffer); return true; + case 0x11: HandlePacketBoatSteer(a_ByteBuffer); return true; + case 0x12: break; // Craft Recipe Request - not yet implemented + case 0x13: HandlePacketPlayerAbilities(a_ByteBuffer); return true; + case 0x14: HandlePacketBlockDig(a_ByteBuffer); return true; + case 0x15: HandlePacketEntityAction(a_ByteBuffer); return true; + case 0x16: HandlePacketSteerVehicle(a_ByteBuffer); return true; + case 0x17: HandlePacketCraftingBookData(a_ByteBuffer); return true; + case 0x18: break; // Resource pack status - not yet implemented + case 0x19: HandlePacketAdvancementTab(a_ByteBuffer); return true; + case 0x1a: HandlePacketSlotSelect(a_ByteBuffer); return true; + case 0x1b: HandlePacketCreativeInventoryAction(a_ByteBuffer); return true; + case 0x1c: HandlePacketUpdateSign(a_ByteBuffer); return true; + case 0x1d: HandlePacketAnimation(a_ByteBuffer); return true; + case 0x1e: HandlePacketSpectate(a_ByteBuffer); return true; + case 0x1f: HandlePacketBlockPlace(a_ByteBuffer); return true; + case 0x20: HandlePacketUseItem(a_ByteBuffer); return true; + } + break; + } + default: + { + // Received a packet in an unknown state, report: + LOGWARNING("Received a packet in an unknown protocol state %d. Ignoring further packets.", m_State); + + // Cannot kick the client - we don't know this state and thus the packet number for the kick packet + + // Switch to a state when all further packets are silently ignored: + m_State = 255; + return false; + } + case 255: + { + // This is the state used for "not processing packets anymore" when we receive a bad packet from a client. + // Do not output anything (the caller will do that for us), just return failure + return false; + } + } // switch (m_State) + + // Unknown packet type, report to the ClientHandle: + m_Client->PacketUnknown(a_PacketType); + return false; +} + + + + diff --git a/src/Protocol/Protocol_1_12.h b/src/Protocol/Protocol_1_12.h index 4d90e2183..53ff05aa6 100644 --- a/src/Protocol/Protocol_1_12.h +++ b/src/Protocol/Protocol_1_12.h @@ -5,6 +5,8 @@ Declares the 1.12 protocol classes: - cProtocol_1_12 - release 1.12 protocol (#335) + - cProtocol_1_12_1 + - release 1.12.1 protocol (#338) (others may be added later in the future for the 1.12 release series) */ @@ -69,3 +71,60 @@ protected: virtual void WriteEntityMetadata(cPacketizer & a_Pkt, const cEntity & a_Entity) override; virtual void WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_Mob) override; }; + + + + + +class cProtocol_1_12_1 : + public cProtocol_1_12 +{ + typedef cProtocol_1_12 super; + +public: + cProtocol_1_12_1(cClientHandle * a_Client, const AString &a_ServerAddress, UInt16 a_ServerPort, UInt32 a_State); + + virtual void SendRespawn(eDimension a_Dimension) override; + virtual void SendPlayerListAddPlayer(const cPlayer & a_Player) override; + virtual void SendPlayerListRemovePlayer(const cPlayer & a_Player) override; + virtual void SendPlayerListUpdateGameMode(const cPlayer & a_Player) override; + virtual void SendPlayerListUpdatePing(const cPlayer & a_Player) override; + virtual void SendPlayerListUpdateDisplayName(const cPlayer & a_Player, const AString & a_CustomName) override; + virtual void SendPlayerAbilities(void) override; + virtual void SendPlayerMoveLook(void) override; + virtual void SendUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ) override; + virtual void SendDestroyEntity(const cEntity & a_Entity) override; + virtual void SendRemoveEntityEffect(const cEntity & a_Entity, int a_EffectID) override; + virtual void SendEntityHeadLook(const cEntity & a_Entity) override; + virtual void SendCameraSetTo(const cEntity & a_Entity) override; + virtual void SendDisplayObjective(const AString & a_Objective, cScoreboard::eDisplaySlot a_Display) override; + virtual void SendEntityMetadata(const cEntity & a_Entity) override; + virtual void SendEntityVelocity(const cEntity & a_Entity) override; + virtual void SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override; + virtual void SendExperience(void) override; + virtual void SendHealth(void) override; + virtual void SendScoreboardObjective(const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) override; + virtual void SendAttachEntity(const cEntity & a_Entity, const cEntity & a_Vehicle) override; + virtual void SendDetachEntity(const cEntity & a_Entity, const cEntity & a_PreviousVehicle) override; + virtual void SendScoreUpdate(const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode) override; + virtual void SendLogin(const cPlayer & a_Player, const cWorld & a_World) override; + virtual void SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay, bool a_DoDaylightCycle) override; + virtual void SendHideTitle(void) override; + virtual void SendResetTitle(void) override; + virtual void SendSetRawSubTitle(const AString & a_SubTitle) override; + virtual void SendSetRawTitle(const AString & a_Title) override; + virtual void SendTitleTimes(int a_FadeInTicks, int a_DisplayTicks, int a_FadeOutTicks) override; + virtual void SendCollectEntity(const cEntity & a_Entity, const cPlayer & a_Player, int a_Count) override; + virtual void SendTeleportEntity(const cEntity & a_Entity) override; + virtual void SendEntityProperties(const cEntity & a_Entity) override; + virtual void SendEntityEffect(const cEntity & a_Entity, int a_EffectID, int a_Amplifier, short a_Duration) override; + virtual void SendPlayerMaxSpeed(void) override; + +protected: + virtual bool HandlePacket(cByteBuffer & a_ByteBuffer, UInt32 a_PacketType) override; + virtual void HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer) override; +}; + + + +