From ff2c246de28689f086d1ea8f73be8c9d52c53ed0 Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Fri, 17 Jul 2020 18:46:50 +0100 Subject: [PATCH] cProtocolRecognizer goes on a diet (#4770) - Removed inheritance from cProtocol --- src/ClientHandle.cpp | 22 +- src/ClientHandle.h | 3 +- src/Protocol/ProtocolRecognizer.cpp | 1202 ++++----------------------- src/Protocol/ProtocolRecognizer.h | 166 ++-- src/Protocol/Protocol_1_11.cpp | 4 +- src/Protocol/Protocol_1_12.cpp | 6 +- src/Protocol/Protocol_1_13.cpp | 2 +- src/Protocol/Protocol_1_9.h | 2 - src/Root.cpp | 2 +- src/Server.cpp | 2 +- 10 files changed, 247 insertions(+), 1164 deletions(-) diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index 9fbc9f89d..4aa3dd9d7 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -32,7 +32,7 @@ #include "Root.h" #include "Protocol/Authenticator.h" -#include "Protocol/ProtocolRecognizer.h" +#include "Protocol/Protocol.h" #include "CompositeChat.h" #include "Items/ItemSword.h" @@ -100,8 +100,6 @@ cClientHandle::cClientHandle(const AString & a_IPString, int a_ViewDistance) : m_LastPlacedSign(0, -1, 0), m_ProtocolVersion(0) { - m_Protocol = cpp14::make_unique(this); - s_ClientCount++; // Not protected by CS because clients are always constructed from the same thread m_UniqueID = s_ClientCount; m_PingStartTime = std::chrono::steady_clock::now(); @@ -141,8 +139,6 @@ cClientHandle::~cClientHandle() m_Player = nullptr; } - m_Protocol.reset(); - LOGD("ClientHandle at %p deleted", static_cast(this)); } @@ -2489,7 +2485,7 @@ void cClientHandle::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializ return; } - if (m_Protocol == nullptr) + if (!m_Protocol.VersionRecognitionSuccessful()) { // TODO (#2588): investigate if and why this occurs return; @@ -2542,7 +2538,7 @@ void cClientHandle::SendDisconnect(const AString & a_Reason) if (!m_HasSentDC) { LOGD("Sending a DC: \"%s\"", StripColorCodes(a_Reason).c_str()); - m_Protocol->SendDisconnect(a_Reason); + m_Protocol.SendDisconnect(*this, a_Reason); m_HasSentDC = true; // csKicked means m_Link will be shut down on the next tick. The // disconnect packet data is sent in the tick thread so the connection @@ -3385,9 +3381,10 @@ void cClientHandle::ProcessProtocolInOut(void) cCSLock Lock(m_CSIncomingData); std::swap(IncomingData, m_IncomingData); } + if (!IncomingData.empty()) { - m_Protocol->DataReceived(IncomingData.data(), IncomingData.size()); + m_Protocol.HandleIncomingData(*this, IncomingData); } // Send any queued outgoing data: @@ -3396,10 +3393,13 @@ void cClientHandle::ProcessProtocolInOut(void) cCSLock Lock(m_CSOutgoingData); std::swap(OutgoingData, m_OutgoingData); } - auto link = m_Link; - if ((link != nullptr) && !OutgoingData.empty()) + + // Capture the link to prevent it being reset between the null check and the Send: + auto Link = m_Link; + + if ((Link != nullptr) && !OutgoingData.empty()) { - link->Send(OutgoingData.data(), OutgoingData.size()); + Link->Send(OutgoingData.data(), OutgoingData.size()); } } diff --git a/src/ClientHandle.h b/src/ClientHandle.h index 1d988b137..dd5d54097 100644 --- a/src/ClientHandle.h +++ b/src/ClientHandle.h @@ -17,6 +17,7 @@ #include "ChunkSender.h" #include "EffectID.h" #include "Protocol/ForgeHandshake.h" +#include "Protocol/ProtocolRecognizer.h" #include "UUID.h" @@ -437,7 +438,7 @@ private: std::unordered_set m_ChunksToSend; // Chunks that need to be sent to the player (queued because they weren't generated yet or there's not enough time to send them) cChunkCoordsList m_SentChunks; // Chunks that are currently sent to the client - std::unique_ptr m_Protocol; + cMultiVersionProtocol m_Protocol; /** Protects m_IncomingData against multithreaded access. */ cCriticalSection m_CSIncomingData; diff --git a/src/Protocol/ProtocolRecognizer.cpp b/src/Protocol/ProtocolRecognizer.cpp index 3f3982c90..a1066a609 100644 --- a/src/Protocol/ProtocolRecognizer.cpp +++ b/src/Protocol/ProtocolRecognizer.cpp @@ -13,12 +13,10 @@ #include "Protocol_1_11.h" #include "Protocol_1_12.h" #include "Protocol_1_13.h" -#include "Packetizer.h" #include "../ClientHandle.h" #include "../Root.h" #include "../Server.h" #include "../World.h" -#include "../ChatColor.h" #include "../JsonUtils.h" #include "../Bindings/PluginManager.h" @@ -26,10 +24,33 @@ -cProtocolRecognizer::cProtocolRecognizer(cClientHandle * a_Client) : - Super(a_Client), - m_Buffer(8192), // We need a larger buffer to support BungeeCord - it sends one huge packet at the start - m_InPingForUnrecognizedVersion(false) +struct sUnsupportedButPingableProtocolException : public std::runtime_error +{ + explicit sUnsupportedButPingableProtocolException() : + std::runtime_error("") + { + } +}; + + + + + +struct sTriedToJoinWithUnsupportedProtocolException : public std::runtime_error +{ + explicit sTriedToJoinWithUnsupportedProtocolException(const std::string & a_Message) : + std::runtime_error(a_Message) + { + } +}; + + + + + +cMultiVersionProtocol::cMultiVersionProtocol() : + HandleIncomingData(std::bind(&cMultiVersionProtocol::HandleIncomingDataInRecognitionStage, this, std::placeholders::_1, std::placeholders::_2)), + m_Buffer(8 KiB) // We need a larger buffer to support BungeeCord - it sends one huge packet at the start { } @@ -37,7 +58,7 @@ cProtocolRecognizer::cProtocolRecognizer(cClientHandle * a_Client) : -AString cProtocolRecognizer::GetVersionTextFromInt(int a_ProtocolVersion) +AString cMultiVersionProtocol::GetVersionTextFromInt(int a_ProtocolVersion) { switch (a_ProtocolVersion) { @@ -62,43 +83,66 @@ AString cProtocolRecognizer::GetVersionTextFromInt(int a_ProtocolVersion) -void cProtocolRecognizer::DataReceived(const char * a_Data, size_t a_Size) +void cMultiVersionProtocol::HandleIncomingDataInRecognitionStage(cClientHandle & a_Client, std::string_view a_Data) { - if (m_Protocol != nullptr) + // We read more than the handshake packet here, oh well. + if (!m_Buffer.Write(a_Data.data(), a_Data.size())) { - // Protocol was already recognized, send to the handler: - m_Protocol->DataReceived(a_Data, a_Size); + a_Client.Kick("Your client sent too much data; please try again later."); return; } - if (!m_Buffer.Write(a_Data, a_Size)) + try { - m_Client->Kick("Unsupported protocol version"); - return; - } + // Note that a_Data is assigned to a subview containing the data to pass to m_Protocol or UnsupportedPing - if (!m_InPingForUnrecognizedVersion) - { - if (TryRecognizeProtocol()) + TryRecognizeProtocol(a_Client, a_Data); + if (m_Protocol == nullptr) { - // The protocol has just been recognized, dump the whole m_Buffer contents into it for parsing: - AString Dump; m_Buffer.ResetRead(); - m_Buffer.ReadAll(Dump); - m_Protocol->DataReceived(Dump.data(), Dump.size()); return; } - else - { - m_Buffer.ResetRead(); - } - } - if (!m_InPingForUnrecognizedVersion) + // The protocol recogniser succesfully identified, switch mode: + HandleIncomingData = [this](cClientHandle &, const std::string_view a_In) + { + // TODO: make it take our a_ReceivedData + m_Protocol->DataReceived(a_In.data(), a_In.size()); + }; + } + catch (const sUnsupportedButPingableProtocolException &) { + // Got a server list ping for an unrecognised version, + // switch into responding to unknown protocols mode: + HandleIncomingData = [this](cClientHandle & a_Clyent, const std::string_view a_In) + { + HandleIncomingDataInOldPingResponseStage(a_Clyent, a_In); + }; + } + catch (const std::exception & Oops) + { + a_Client.Kick(Oops.what()); return; } + // Explicitly process any remaining data with the new handler: + HandleIncomingData(a_Client, a_Data); +} + + + + + +void cMultiVersionProtocol::HandleIncomingDataInOldPingResponseStage(cClientHandle & a_Client, const std::string_view a_Data) +{ + if (!m_Buffer.Write(a_Data.data(), a_Data.size())) + { + a_Client.Kick("Server list ping failed, too much data."); + return; + } + + cByteBuffer OutPacketBuffer(6 KiB); + // Handle server list ping packets for (;;) { @@ -117,17 +161,21 @@ void cProtocolRecognizer::DataReceived(const char * a_Data, size_t a_Size) if ((PacketID == 0x00) && (PacketLen == 1)) // Request packet { - HandlePacketStatusRequest(); + HandlePacketStatusRequest(a_Client, OutPacketBuffer); + SendPacket(a_Client, OutPacketBuffer); } else if ((PacketID == 0x01) && (PacketLen == 9)) // Ping packet { - HandlePacketStatusPing(); + HandlePacketStatusPing(a_Client, OutPacketBuffer); + SendPacket(a_Client, OutPacketBuffer); } else { - m_Client->Kick("Server list ping failed, unrecognized packet"); + a_Client.Kick("Server list ping failed, unrecognized packet."); return; } + + m_Buffer.CommitRead(); } } @@ -135,1031 +183,135 @@ void cProtocolRecognizer::DataReceived(const char * a_Data, size_t a_Size) -void cProtocolRecognizer::SendAttachEntity(const cEntity & a_Entity, const cEntity & a_Vehicle) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendAttachEntity(a_Entity, a_Vehicle); -} - - - - - -void cProtocolRecognizer::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendBlockAction(a_BlockX, a_BlockY, a_BlockZ, a_Byte1, a_Byte2, a_BlockType); -} - - - - - -void cProtocolRecognizer::SendBlockBreakAnim(UInt32 a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendBlockBreakAnim(a_EntityID, a_BlockX, a_BlockY, a_BlockZ, a_Stage); -} - - - - - -void cProtocolRecognizer::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendBlockChange(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta); -} - - - - - -void cProtocolRecognizer::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendBlockChanges(a_ChunkX, a_ChunkZ, a_Changes); -} - - - - - -void cProtocolRecognizer::SendCameraSetTo(const cEntity & a_Entity) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendCameraSetTo(a_Entity); -} - - - - - -void cProtocolRecognizer::SendChat(const AString & a_Message, eChatType a_Type) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendChat(a_Message, a_Type); -} - - - - - -void cProtocolRecognizer::SendChat(const cCompositeChat & a_Message, eChatType a_Type, bool a_ShouldUseChatPrefixes) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendChat(a_Message, a_Type, a_ShouldUseChatPrefixes); -} - - - - - -void cProtocolRecognizer::SendChatRaw(const AString & a_MessageRaw, eChatType a_Type) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendChatRaw(a_MessageRaw, a_Type); -} - - - - - -void cProtocolRecognizer::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendChunkData(a_ChunkX, a_ChunkZ, a_Serializer); -} - - - - - -void cProtocolRecognizer::SendCollectEntity(const cEntity & a_Collected, const cEntity & a_Collector, unsigned a_Count) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendCollectEntity(a_Collected, a_Collector, a_Count); -} - - - - - -void cProtocolRecognizer::SendDestroyEntity(const cEntity & a_Entity) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendDestroyEntity(a_Entity); -} - - - - - -void cProtocolRecognizer::SendDetachEntity(const cEntity & a_Entity, const cEntity & a_PreviousVehicle) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendDetachEntity(a_Entity, a_PreviousVehicle); -} - - - - - -void cProtocolRecognizer::SendDisconnect(const AString & a_Reason) +void cMultiVersionProtocol::SendDisconnect(cClientHandle & a_Client, const AString & a_Reason) { if (m_Protocol != nullptr) { m_Protocol->SendDisconnect(a_Reason); + return; } - else - { - AString Message = Printf("{\"text\":\"%s\"}", EscapeString(a_Reason).c_str()); - cPacketizer Pkt(*this, pktDisconnectDuringLogin); - Pkt.WriteString(Message); - } -} - - - - - -void cProtocolRecognizer::SendEditSign(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendEditSign(a_BlockX, a_BlockY, a_BlockZ); -} - - - - - -void cProtocolRecognizer::SendEntityAnimation(const cEntity & a_Entity, char a_Animation) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendEntityAnimation(a_Entity, a_Animation); -} - - - - - -void cProtocolRecognizer::SendEntityEffect(const cEntity & a_Entity, int a_EffectID, int a_Amplifier, int a_Duration) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendEntityEffect(a_Entity, a_EffectID, a_Amplifier, a_Duration); -} - - - - - -void cProtocolRecognizer::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendEntityEquipment(a_Entity, a_SlotNum, a_Item); -} - - - - - -void cProtocolRecognizer::SendEntityHeadLook(const cEntity & a_Entity) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendEntityHeadLook(a_Entity); -} - - - - - -void cProtocolRecognizer::SendEntityLook(const cEntity & a_Entity) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendEntityLook(a_Entity); -} - - - - - -void cProtocolRecognizer::SendEntityMetadata(const cEntity & a_Entity) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendEntityMetadata(a_Entity); -} - - - - - -void cProtocolRecognizer::SendEntityPosition(const cEntity & a_Entity) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendEntityPosition(a_Entity); -} - - - - - -void cProtocolRecognizer::SendEntityProperties(const cEntity & a_Entity) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendEntityProperties(a_Entity); -} - - - - - -void cProtocolRecognizer::SendEntityStatus(const cEntity & a_Entity, char a_Status) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendEntityStatus(a_Entity, a_Status); -} - - - - - -void cProtocolRecognizer::SendEntityVelocity(const cEntity & a_Entity) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendEntityVelocity(a_Entity); -} - - - - - -void cProtocolRecognizer::SendExplosion(double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendExplosion(a_BlockX, a_BlockY, a_BlockZ, a_Radius, a_BlocksAffected, a_PlayerMotion); -} - - - - - -void cProtocolRecognizer::SendGameMode(eGameMode a_GameMode) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendGameMode(a_GameMode); -} - - - - - -void cProtocolRecognizer::SendHealth(void) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendHealth(); -} - - - - - -void cProtocolRecognizer::SendHeldItemChange(int a_ItemIndex) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendHeldItemChange(a_ItemIndex); -} - - - - - -void cProtocolRecognizer::SendHideTitle(void) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendHideTitle(); -} - - - - - -void cProtocolRecognizer::SendWindowProperty(const cWindow & a_Window, short a_Property, short a_Value) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendWindowProperty(a_Window, a_Property, a_Value); -} - - - - - -void cProtocolRecognizer::SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem & a_Item) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendInventorySlot(a_WindowID, a_SlotNum, a_Item); -} - - - - - -void cProtocolRecognizer::SendKeepAlive(UInt32 a_PingID) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendKeepAlive(a_PingID); -} - - - - - -void cProtocolRecognizer::SendLeashEntity(const cEntity & a_Entity, const cEntity & a_EntityLeashedTo) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendLeashEntity(a_Entity, a_EntityLeashedTo); -} - - - - - -void cProtocolRecognizer::SendUnleashEntity(const cEntity & a_Entity) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendUnleashEntity(a_Entity); -} - - - - - -void cProtocolRecognizer::SendLogin(const cPlayer & a_Player, const cWorld & a_World) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendLogin(a_Player, a_World); -} - - - - - -void cProtocolRecognizer::SendLoginSuccess(void) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendLoginSuccess(); -} - - - - - -void cProtocolRecognizer::SendMapData(const cMap & a_Map, int a_DataStartX, int a_DataStartY) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendMapData(a_Map, a_DataStartX, a_DataStartY); -} - - - - - -void cProtocolRecognizer::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) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendParticleEffect(a_ParticleName, a_SrcX, a_SrcY, a_SrcZ, a_OffsetX, a_OffsetY, a_OffsetZ, a_ParticleData, a_ParticleAmount); -} - - - - - -void cProtocolRecognizer::SendParticleEffect(const AString & a_ParticleName, Vector3f a_Src, Vector3f a_Offset, float a_ParticleData, int a_ParticleAmount, std::array a_Data) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendParticleEffect(a_ParticleName, a_Src, a_Offset, a_ParticleData, a_ParticleAmount, a_Data); -} - - - - - -void cProtocolRecognizer::SendPaintingSpawn(const cPainting & a_Painting) -{ - m_Protocol->SendPaintingSpawn(a_Painting); -} - - - - - -void cProtocolRecognizer::SendPlayerAbilities(void) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendPlayerAbilities(); -} - - - - - -void cProtocolRecognizer::SendPlayerListAddPlayer(const cPlayer & a_Player) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendPlayerListAddPlayer(a_Player); -} - - - - - -void cProtocolRecognizer::SendPlayerListRemovePlayer(const cPlayer & a_Player) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendPlayerListRemovePlayer(a_Player); -} - - - - - -void cProtocolRecognizer::SendPlayerListUpdateGameMode(const cPlayer & a_Player) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendPlayerListUpdateGameMode(a_Player); -} - - - - - -void cProtocolRecognizer::SendPlayerListUpdatePing(const cPlayer & a_Player) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendPlayerListUpdatePing(a_Player); -} - - - - - -void cProtocolRecognizer::SendPlayerListUpdateDisplayName(const cPlayer & a_Player, const AString & a_CustomName) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendPlayerListUpdateDisplayName(a_Player, a_CustomName); -} - - - - - -void cProtocolRecognizer::SendPlayerMaxSpeed(void) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendPlayerMaxSpeed(); -} - - - - - -void cProtocolRecognizer::SendPlayerMoveLook(void) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendPlayerMoveLook(); -} - - - - - -void cProtocolRecognizer::SendPlayerPosition(void) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendPlayerPosition(); -} - - - - - -void cProtocolRecognizer::SendPlayerSpawn(const cPlayer & a_Player) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendPlayerSpawn(a_Player); -} - - - - - -void cProtocolRecognizer::SendPluginMessage(const AString & a_Channel, const AString & a_Message) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendPluginMessage(a_Channel, a_Message); -} - - - - - -void cProtocolRecognizer::SendRemoveEntityEffect(const cEntity & a_Entity, int a_EffectID) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendRemoveEntityEffect(a_Entity, a_EffectID); -} - - - - - -void cProtocolRecognizer::SendResetTitle(void) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendResetTitle(); -} - - - - - -void cProtocolRecognizer::SendResourcePack(const AString & a_ResourcePackUrl) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendResourcePack(a_ResourcePackUrl); -} - - - - - -void cProtocolRecognizer::SendRespawn(eDimension a_Dimension) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendRespawn(a_Dimension); -} - - - - - -void cProtocolRecognizer::SendExperience(void) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendExperience(); -} - - - - - -void cProtocolRecognizer::SendExperienceOrb(const cExpOrb & a_ExpOrb) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendExperienceOrb(a_ExpOrb); -} - - - - - -void cProtocolRecognizer::SendScoreboardObjective(const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendScoreboardObjective(a_Name, a_DisplayName, a_Mode); -} - - - - - -void cProtocolRecognizer::SendScoreUpdate(const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendScoreUpdate(a_Objective, a_Player, a_Score, a_Mode); -} - - - - - -void cProtocolRecognizer::SendDisplayObjective(const AString & a_Objective, cScoreboard::eDisplaySlot a_Display) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendDisplayObjective(a_Objective, a_Display); -} - - - - - -void cProtocolRecognizer::SendSetSubTitle(const cCompositeChat & a_SubTitle) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendSetSubTitle(a_SubTitle); -} - - - - - -void cProtocolRecognizer::SendSetRawSubTitle(const AString & a_SubTitle) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendSetRawSubTitle(a_SubTitle); -} - - - - - -void cProtocolRecognizer::SendSetTitle(const cCompositeChat & a_Title) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendSetTitle(a_Title); -} - - - - - -void cProtocolRecognizer::SendSetRawTitle(const AString & a_Title) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendSetRawTitle(a_Title); -} - - - - - -void cProtocolRecognizer::SendSoundEffect(const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendSoundEffect(a_SoundName, a_X, a_Y, a_Z, a_Volume, a_Pitch); -} - - - - - -void cProtocolRecognizer::SendSoundParticleEffect(const EffectID a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendSoundParticleEffect(a_EffectID, a_SrcX, a_SrcY, a_SrcZ, a_Data); -} - - - - - -void cProtocolRecognizer::SendSpawnEntity(const cEntity & a_Entity) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendSpawnEntity(a_Entity); -} - - - - - -void cProtocolRecognizer::SendSpawnMob(const cMonster & a_Mob) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendSpawnMob(a_Mob); -} - - - - - -void cProtocolRecognizer::SendStatistics(const cStatManager & a_Manager) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendStatistics(a_Manager); -} - - - - - -void cProtocolRecognizer::SendTabCompletionResults(const AStringVector & a_Results) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendTabCompletionResults(a_Results); -} - - - - - -void cProtocolRecognizer::SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendThunderbolt(a_BlockX, a_BlockY, a_BlockZ); -} - - - - - -void cProtocolRecognizer::SendTitleTimes(int a_FadeInTicks, int a_DisplayTicks, int a_FadeOutTicks) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendTitleTimes(a_FadeInTicks, a_DisplayTicks, a_FadeOutTicks); -} - - - - - -void cProtocolRecognizer::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay, bool a_DoDaylightCycle) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendTimeUpdate(a_WorldAge, a_TimeOfDay, a_DoDaylightCycle); -} - - - - - -void cProtocolRecognizer::SendUnloadChunk(int a_ChunkX, int a_ChunkZ) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendUnloadChunk(a_ChunkX, a_ChunkZ); -} - - - - - -void cProtocolRecognizer::SendUpdateBlockEntity(cBlockEntity & a_BlockEntity) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendUpdateBlockEntity(a_BlockEntity); -} - - - - - -void cProtocolRecognizer::SendUpdateSign(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendUpdateSign(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4); -} - - - - - -void cProtocolRecognizer::SendUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendUseBed(a_Entity, a_BlockX, a_BlockY, a_BlockZ); -} - - - - - -void cProtocolRecognizer::SendUnlockRecipe(UInt32 a_RecipeID) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendUnlockRecipe(a_RecipeID); -} - - - - - -void cProtocolRecognizer::SendInitRecipes(UInt32 a_RecipeID) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendInitRecipes(a_RecipeID); -} - - - - - -void cProtocolRecognizer::SendWeather(eWeather a_Weather) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendWeather(a_Weather); -} - - - - - -void cProtocolRecognizer::SendWholeInventory(const cWindow & a_Window) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendWholeInventory(a_Window); -} - - - - - -void cProtocolRecognizer::SendWindowClose(const cWindow & a_Window) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendWindowClose(a_Window); -} - - - - - -void cProtocolRecognizer::SendWindowOpen(const cWindow & a_Window) -{ - ASSERT(m_Protocol != nullptr); - m_Protocol->SendWindowOpen(a_Window); -} - - - - - -AString cProtocolRecognizer::GetAuthServerID(void) -{ - ASSERT(m_Protocol != nullptr); - return m_Protocol->GetAuthServerID(); -} - - - + const AString Message = Printf("{\"text\":\"%s\"}", EscapeString(a_Reason).c_str()); + const auto PacketID = GetPacketID(cProtocol::ePacketType::pktDisconnectDuringLogin); + cByteBuffer Out( + cByteBuffer::GetVarIntSize(PacketID) + + cByteBuffer::GetVarIntSize(static_cast(Message.size())) + Message.size() + ); -void cProtocolRecognizer::SendData(const char * a_Data, size_t a_Size) -{ - // This is used only when handling the server ping - m_Client->SendData(a_Data, a_Size); + VERIFY(Out.WriteVarInt32(PacketID)); + VERIFY(Out.WriteVarUTF8String(Message)); + SendPacket(a_Client, Out); } -bool cProtocolRecognizer::TryRecognizeProtocol(void) +void cMultiVersionProtocol::TryRecognizeProtocol(cClientHandle & a_Client, std::string_view & a_Data) { // NOTE: If a new protocol is added or an old one is removed, adjust MCS_CLIENT_VERSIONS and MCS_PROTOCOL_VERSIONS macros in the header file // Lengthed protocol, try if it has the entire initial handshake packet: UInt32 PacketLen; - UInt32 ReadSoFar = static_cast(m_Buffer.GetReadableSpace()); if (!m_Buffer.ReadVarInt(PacketLen)) { // Not enough bytes for the packet length, keep waiting - return false; + return; } - ReadSoFar -= static_cast(m_Buffer.GetReadableSpace()); + if (!m_Buffer.CanReadBytes(PacketLen)) { // Not enough bytes for the packet, keep waiting - return false; - } - if (!TryRecognizeLengthedProtocol(PacketLen - ReadSoFar)) - { - return false; + // More of a sanity check to make sure no one tries anything funny (since ReadXXX can wait for data themselves): + return; } - // The protocol has been recognized, initialize it: + m_Protocol = TryRecognizeLengthedProtocol(a_Client, a_Data); ASSERT(m_Protocol != nullptr); - try - { - m_Protocol->Initialize(*m_Client); - } - catch (const std::exception & exc) - { - m_Client->Kick(exc.what()); - m_Protocol.reset(); - return false; - } - return true; + + // The protocol has been recognized, initialize it: + m_Protocol->Initialize(a_Client); } -bool cProtocolRecognizer::TryRecognizeLengthedProtocol(UInt32 a_PacketLengthRemaining) +std::unique_ptr cMultiVersionProtocol::TryRecognizeLengthedProtocol(cClientHandle & a_Client, std::string_view & a_Data) { UInt32 PacketType; - if (!m_Buffer.ReadVarInt(PacketType)) - { - return false; - } - if (PacketType != 0x00) - { - // Not an initial handshake packet, we don't know how to talk to them - LOGINFO("Client \"%s\" uses an unsupported protocol (lengthed, initial packet %u)", - m_Client->GetIPString().c_str(), PacketType - ); - m_Client->Kick("Unsupported protocol version"); - return false; - } UInt32 ProtocolVersion; - if (!m_Buffer.ReadVarInt(ProtocolVersion)) - { - return false; - } - m_Client->SetProtocolVersion(ProtocolVersion); AString ServerAddress; UInt16 ServerPort; UInt32 NextState; - if (!m_Buffer.ReadVarUTF8String(ServerAddress)) + + if (!m_Buffer.ReadVarInt(PacketType) || (PacketType != 0x00)) { - return false; + // Not an initial handshake packet, we don't know how to talk to them + LOGINFO("Client \"%s\" uses an unsupported protocol (lengthed, initial packet %u)", + a_Client.GetIPString().c_str(), PacketType + ); + + throw sTriedToJoinWithUnsupportedProtocolException( + Printf("Your client isn't supported.\nTry connecting with Minecraft " MCS_CLIENT_VERSIONS, ProtocolVersion) + ); } - if (!m_Buffer.ReadBEUInt16(ServerPort)) + + if ( + !m_Buffer.ReadVarInt(ProtocolVersion) || + !m_Buffer.ReadVarUTF8String(ServerAddress) || + !m_Buffer.ReadBEUInt16(ServerPort) || + !m_Buffer.ReadVarInt(NextState) + ) { - return false; - } - if (!m_Buffer.ReadVarInt(NextState)) - { - return false; + // TryRecognizeProtocol guarantees that we will have as much + // data to read as the client claims in the protocol length field: + throw sTriedToJoinWithUnsupportedProtocolException("Incorrect amount of data received - hacked client?"); } + + // TODO: this should be a protocol property, not ClientHandle: + a_Client.SetProtocolVersion(ProtocolVersion); + + // The protocol has just been recognized, advance data start + // to after the handshake and leave the rest to the protocol: + a_Data = a_Data.substr(m_Buffer.GetUsedSpace() - m_Buffer.GetReadableSpace()); + + // We read more than we can handle, purge the rest: + [[maybe_unused]] const bool Success = + m_Buffer.SkipRead(m_Buffer.GetReadableSpace()); + ASSERT(Success); + + // All good, eat up the data: m_Buffer.CommitRead(); + switch (ProtocolVersion) { - case PROTO_VERSION_1_8_0: - { - m_Buffer.CommitRead(); - m_Protocol.reset(new cProtocol_1_8_0(m_Client, ServerAddress, ServerPort, NextState)); - return true; - } - case PROTO_VERSION_1_9_0: - { - m_Protocol.reset(new cProtocol_1_9_0(m_Client, ServerAddress, ServerPort, NextState)); - return true; - } - case PROTO_VERSION_1_9_1: - { - m_Protocol.reset(new cProtocol_1_9_1(m_Client, ServerAddress, ServerPort, NextState)); - return true; - } - case PROTO_VERSION_1_9_2: - { - m_Protocol.reset(new cProtocol_1_9_2(m_Client, ServerAddress, ServerPort, NextState)); - return true; - } - case PROTO_VERSION_1_9_4: - { - m_Protocol.reset(new cProtocol_1_9_4(m_Client, ServerAddress, ServerPort, NextState)); - return true; - } - case PROTO_VERSION_1_10_0: - { - m_Protocol.reset(new cProtocol_1_10_0(m_Client, ServerAddress, ServerPort, NextState)); - return true; - } - case PROTO_VERSION_1_11_0: - { - m_Protocol.reset(new cProtocol_1_11_0(m_Client, ServerAddress, ServerPort, NextState)); - return true; - } - case PROTO_VERSION_1_11_1: - { - m_Protocol.reset(new cProtocol_1_11_1(m_Client, ServerAddress, ServerPort, NextState)); - return true; - } - case PROTO_VERSION_1_12: - { - m_Protocol.reset(new cProtocol_1_12(m_Client, ServerAddress, ServerPort, NextState)); - return true; - } - case PROTO_VERSION_1_12_1: - { - m_Protocol.reset(new cProtocol_1_12_1(m_Client, ServerAddress, ServerPort, NextState)); - return true; - } - case PROTO_VERSION_1_12_2: - { - m_Protocol.reset(new cProtocol_1_12_2(m_Client, ServerAddress, ServerPort, NextState)); - return true; - } - case PROTO_VERSION_1_13: - { - m_Protocol.reset(new cProtocol_1_13(m_Client, ServerAddress, ServerPort, NextState)); - return true; - } + case PROTO_VERSION_1_8_0: return std::make_unique(&a_Client, ServerAddress, ServerPort, NextState); + case PROTO_VERSION_1_9_0: return std::make_unique(&a_Client, ServerAddress, ServerPort, NextState); + case PROTO_VERSION_1_9_1: return std::make_unique(&a_Client, ServerAddress, ServerPort, NextState); + case PROTO_VERSION_1_9_2: return std::make_unique(&a_Client, ServerAddress, ServerPort, NextState); + case PROTO_VERSION_1_9_4: return std::make_unique(&a_Client, ServerAddress, ServerPort, NextState); + case PROTO_VERSION_1_10_0: return std::make_unique(&a_Client, ServerAddress, ServerPort, NextState); + case PROTO_VERSION_1_11_0: return std::make_unique(&a_Client, ServerAddress, ServerPort, NextState); + case PROTO_VERSION_1_11_1: return std::make_unique(&a_Client, ServerAddress, ServerPort, NextState); + case PROTO_VERSION_1_12: return std::make_unique(&a_Client, ServerAddress, ServerPort, NextState); + case PROTO_VERSION_1_12_1: return std::make_unique(&a_Client, ServerAddress, ServerPort, NextState); + case PROTO_VERSION_1_12_2: return std::make_unique(&a_Client, ServerAddress, ServerPort, NextState); + case PROTO_VERSION_1_13: return std::make_unique(&a_Client, ServerAddress, ServerPort, NextState); default: { LOGD("Client \"%s\" uses an unsupported protocol (lengthed, version %u (0x%x))", - m_Client->GetIPString().c_str(), ProtocolVersion, ProtocolVersion + a_Client.GetIPString().c_str(), ProtocolVersion, ProtocolVersion ); + if (NextState != 1) { - m_Client->Kick(Printf("Unsupported protocol version %u, please use one of these versions:\n" MCS_CLIENT_VERSIONS, ProtocolVersion)); - return false; + throw sTriedToJoinWithUnsupportedProtocolException( + Printf("Unsupported protocol version %u.\nTry connecting with Minecraft " MCS_CLIENT_VERSIONS, ProtocolVersion) + ); } - else - { - m_InPingForUnrecognizedVersion = true; - } - return false; + + throw sUnsupportedButPingableProtocolException(); } } } @@ -1168,39 +320,39 @@ bool cProtocolRecognizer::TryRecognizeLengthedProtocol(UInt32 a_PacketLengthRema -void cProtocolRecognizer::SendPacket(cPacketizer & a_Pkt) +void cMultiVersionProtocol::SendPacket(cClientHandle & a_Client, cByteBuffer & a_OutPacketBuffer) { // Writes out the packet normally. - UInt32 PacketLen = static_cast(m_OutPacketBuffer.GetUsedSpace()); - AString PacketData, CompressedPacket; - m_OutPacketBuffer.ReadAll(PacketData); - m_OutPacketBuffer.CommitRead(); + UInt32 PacketLen = static_cast(a_OutPacketBuffer.GetUsedSpace()); + cByteBuffer OutPacketLenBuffer(cByteBuffer::GetVarIntSize(PacketLen)); // Compression doesn't apply to this state, send raw data: - m_OutPacketLenBuffer.WriteVarInt32(PacketLen); + VERIFY(OutPacketLenBuffer.WriteVarInt32(PacketLen)); AString LengthData; - m_OutPacketLenBuffer.ReadAll(LengthData); - SendData(LengthData.data(), LengthData.size()); + OutPacketLenBuffer.ReadAll(LengthData); + a_Client.SendData(LengthData.data(), LengthData.size()); - // Send the packet's payload - m_OutPacketLenBuffer.CommitRead(); - SendData(PacketData.data(), PacketData.size()); + // Send the packet's payload: + AString PacketData; + a_OutPacketBuffer.ReadAll(PacketData); + a_OutPacketBuffer.CommitRead(); + a_Client.SendData(PacketData.data(), PacketData.size()); } -UInt32 cProtocolRecognizer::GetPacketID(ePacketType a_PacketType) +UInt32 cMultiVersionProtocol::GetPacketID(cProtocol::ePacketType a_PacketType) { switch (a_PacketType) { - case pktDisconnectDuringLogin: return 0x00; - case pktStatusResponse: return 0x00; - case pktPingResponse: return 0x01; + case cProtocol::ePacketType::pktDisconnectDuringLogin: return 0x00; + case cProtocol::ePacketType::pktStatusResponse: return 0x00; + case cProtocol::ePacketType::pktPingResponse: return 0x01; default: { - ASSERT(!"cProtocolRecognizer::GetPacketID() called for an unhandled packet"); + ASSERT(!"GetPacketID() called for an unhandled packet"); return 0; } } @@ -1210,14 +362,14 @@ UInt32 cProtocolRecognizer::GetPacketID(ePacketType a_PacketType) -void cProtocolRecognizer::HandlePacketStatusRequest(void) +void cMultiVersionProtocol::HandlePacketStatusRequest(cClientHandle & a_Client, cByteBuffer & a_Out) { 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); + cRoot::Get()->GetPluginManager()->CallHookServerPing(a_Client, ServerDescription, NumPlayers, MaxPlayers, Favicon); // Version: Json::Value Version; @@ -1246,15 +398,15 @@ void cProtocolRecognizer::HandlePacketStatusRequest(void) AString Response = JsonUtils::WriteFastString(ResponseValue); - cPacketizer Pkt(*this, pktStatusResponse); - Pkt.WriteString(Response); + VERIFY(a_Out.WriteVarInt32(GetPacketID(cProtocol::ePacketType::pktStatusResponse))); + VERIFY(a_Out.WriteVarUTF8String(Response)); } -void cProtocolRecognizer::HandlePacketStatusPing() +void cMultiVersionProtocol::HandlePacketStatusPing(cClientHandle & a_Client, cByteBuffer & a_Out) { Int64 Timestamp; if (!m_Buffer.ReadBEInt64(Timestamp)) @@ -1262,6 +414,6 @@ void cProtocolRecognizer::HandlePacketStatusPing() return; } - cPacketizer Pkt(*this, pktPingResponse); - Pkt.WriteBEInt64(Timestamp); + VERIFY(a_Out.WriteVarInt32(GetPacketID(cProtocol::ePacketType::pktPingResponse))); + VERIFY(a_Out.WriteBEInt64(Timestamp)); } diff --git a/src/Protocol/ProtocolRecognizer.h b/src/Protocol/ProtocolRecognizer.h index c5d180b44..9aa31572e 100644 --- a/src/Protocol/ProtocolRecognizer.h +++ b/src/Protocol/ProtocolRecognizer.h @@ -1,14 +1,13 @@ #pragma once #include "Protocol.h" -#include "../ByteBuffer.h" // 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_CLIENT_VERSIONS "1.8.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 @@ -18,12 +17,10 @@ /** Meta-protocol that recognizes multiple protocol versions, creates the specific protocol version instance and redirects everything to it. */ -class cProtocolRecognizer: - public cProtocol +class cMultiVersionProtocol { - using Super = cProtocol; - public: + enum { PROTO_VERSION_1_8_0 = 47, @@ -40,132 +37,67 @@ public: PROTO_VERSION_1_13 = 393 }; - cProtocolRecognizer(cClientHandle * a_Client); - virtual ~cProtocolRecognizer() override {} + cMultiVersionProtocol(); /** Translates protocol version number into protocol version text: 49 -> "1.4.4" */ static AString GetVersionTextFromInt(int a_ProtocolVersion); - /** Called when client sends some data: */ - virtual void DataReceived(const char * a_Data, size_t a_Size) override; + /** Returns if we contain a concrete protocol corresponding to the client's protocol version. */ + bool VersionRecognitionSuccessful() + { + return m_Protocol != nullptr; + } - /** Sending stuff to clients (alphabetically sorted): */ - virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity & a_Vehicle) override; - virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override; - virtual void SendBlockBreakAnim (UInt32 a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override; - 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 SendCameraSetTo (const cEntity & a_Entity) override; - virtual void SendChat (const AString & a_Message, eChatType a_Type) override; - virtual void SendChat (const cCompositeChat & a_Message, eChatType a_Type, bool a_ShouldUseChatPrefixes) override; - virtual void SendChatRaw (const AString & a_MessageRaw, eChatType a_Type) override; - virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override; - virtual void SendCollectEntity (const cEntity & a_Collected, const cEntity & a_Collector, unsigned a_Count) override; - virtual void SendDestroyEntity (const cEntity & a_Entity) override; - virtual void SendDetachEntity (const cEntity & a_Entity, const cEntity & a_PreviousVehicle) override; - virtual void SendDisconnect (const AString & a_Reason) override; - virtual void SendEntityAnimation (const cEntity & a_Entity, char a_Animation) override; - virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) override; ///< Request the client to open up the sign editor for the sign (1.6+) - virtual void SendEntityEffect (const cEntity & a_Entity, int a_EffectID, int a_Amplifier, int a_Duration) override; - virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override; - virtual void SendEntityHeadLook (const cEntity & a_Entity) override; - virtual void SendEntityLook (const cEntity & a_Entity) override; - virtual void SendEntityMetadata (const cEntity & a_Entity) override; - virtual void SendEntityPosition (const cEntity & a_Entity) override; - virtual void SendEntityProperties (const cEntity & a_Entity) override; - virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) override; - virtual void SendEntityVelocity (const cEntity & a_Entity) override; - virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) override; - virtual void SendGameMode (eGameMode a_GameMode) override; - virtual void SendHealth (void) override; - virtual void SendHeldItemChange (int a_ItemIndex) override; - virtual void SendHideTitle (void) override; - virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override; - virtual void SendKeepAlive (UInt32 a_PingID) override; - virtual void SendLeashEntity (const cEntity & a_Entity, const cEntity & a_EntityLeashedTo) override; - virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override; - virtual void SendLoginSuccess (void) override; - virtual void SendMapData (const cMap & a_Map, int a_DataStartX, int a_DataStartY) 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 SendPaintingSpawn (const cPainting & a_Painting) override; - virtual void SendPlayerAbilities (void) 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 SendPlayerMaxSpeed (void) override; - virtual void SendPlayerMoveLook (void) override; - virtual void SendPlayerPosition (void) override; - virtual void SendPlayerSpawn (const cPlayer & a_Player) override; - virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) override; - virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) override; - virtual void SendResetTitle (void) override; - virtual void SendResourcePack (const AString & a_ResourcePackUrl) override; - virtual void SendRespawn (eDimension a_Dimension) override; - virtual void SendExperience (void) override; - virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override; - virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) override; - virtual void SendScoreUpdate (const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode) override; - virtual void SendDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display) override; - virtual void SendSetSubTitle (const cCompositeChat & a_SubTitle) override; - virtual void SendSetRawSubTitle (const AString & a_SubTitle) override; - virtual void SendSetTitle (const cCompositeChat & a_Title) override; - virtual void SendSetRawTitle (const AString & a_Title) 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 SendSoundParticleEffect (const EffectID a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override; - virtual void SendSpawnEntity (const cEntity & a_Entity) override; - virtual void SendSpawnMob (const cMonster & a_Mob) override; - virtual void SendStatistics (const cStatManager & a_Manager) override; - virtual void SendTabCompletionResults (const AStringVector & a_Results) override; - virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override; - virtual void SendTitleTimes (int a_FadeInTicks, int a_DisplayTicks, int a_FadeOutTicks) override; - virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay, bool a_DoDaylightCycle) override; - virtual void SendUnleashEntity (const cEntity & a_Entity) override; - virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override; - virtual void SendUpdateBlockEntity (cBlockEntity & a_BlockEntity) override; - virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) override; - virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ) override; - virtual void SendUnlockRecipe (UInt32 a_RecipeID) override; - virtual void SendInitRecipes (UInt32 a_RecipeID) override; - virtual void SendWeather (eWeather a_Weather) override; - virtual void SendWholeInventory (const cWindow & a_Window) override; - virtual void SendWindowClose (const cWindow & a_Window) override; - virtual void SendWindowOpen (const cWindow & a_Window) override; - virtual void SendWindowProperty (const cWindow & a_Window, short a_Property, short a_Value) override; + /** Convenience overload to enable redirecting sends to the underlying implementation. */ + auto & operator->() + { + return m_Protocol; + } - virtual AString GetAuthServerID(void) override; + /** The function that's responsible for processing incoming protocol data. */ + std::function HandleIncomingData; - virtual void SendData(const char * a_Data, size_t a_Size) override; + /** Sends a disconnect to the client as a result of a recognition error. + This function can be used to disconnect before any protocol has been recognised. */ + void SendDisconnect(cClientHandle & a_Client, const AString & a_Reason); -protected: +private: - /** The recognized protocol */ - std::unique_ptr m_Protocol; + /** Handles data reception in a newly-created client handle that doesn't yet have known protocol. + a_Data contains a view of data that were just received. + Calls TryRecognizeProtocol to populate m_Protocol, and transitions to another mode depending on success. */ + void HandleIncomingDataInRecognitionStage(cClientHandle & a_Client, std::string_view a_Data); - /** Buffer for the incoming data until we recognize the protocol */ - cByteBuffer m_Buffer; + /** Handles and responds to unsupported clients sending pings. */ + void HandleIncomingDataInOldPingResponseStage(cClientHandle & a_Client, const std::string_view a_Data); - /** Is a server list ping for an unrecognized version currently occuring? */ - bool m_InPingForUnrecognizedVersion; + /* Tries to recognize a protocol based on a_Data and m_Buffer contents. + a_Data is replaced with a sub-view, with handshake packet removed. */ + void TryRecognizeProtocol(cClientHandle & a_Client, std::string_view & a_Data); + + /** Tries to recognize a protocol in the lengthed family (1.7+), based on m_Buffer. + The packet length and type have already been read, type is 0. + Returns a cProtocol_XXX instance if recognized. */ + std::unique_ptr TryRecognizeLengthedProtocol(cClientHandle & a_Client, std::string_view & a_Data); + + /** Sends one packet inside a cByteBuffer. + This is used only when handling an outdated server ping. */ + static void SendPacket(cClientHandle & a_Client, cByteBuffer & a_OutPacketBuffer); /** Returns the protocol-specific packet ID given the protocol-agnostic packet enum. */ - virtual UInt32 GetPacketID(ePacketType a_PacketType) override; + static UInt32 GetPacketID(cProtocol::ePacketType a_PacketType); - // Packet handlers while in status state (m_InPingForUnrecognizedVersion == true) - void HandlePacketStatusRequest(); - void HandlePacketStatusPing(); + /* Status handler for unrecognised versions. */ + void HandlePacketStatusRequest(cClientHandle & a_Client, cByteBuffer & a_Out); - /** Tries to recognize protocol based on m_Buffer contents; returns true if recognized */ - bool TryRecognizeProtocol(void); + /* Ping handler for unrecognised versions. */ + void HandlePacketStatusPing(cClientHandle & a_Client, cByteBuffer & a_Out); - /** Tries to recognize a protocol in the lengthed family (1.7+), based on m_Buffer; returns true if recognized. - The packet length and type have already been read, type is 0 - The number of bytes remaining in the packet is passed as a_PacketLengthRemaining. */ - bool TryRecognizeLengthedProtocol(UInt32 a_PacketLengthRemaining); + /** The actual protocol implementation. + Created when recognition of the client version succeeds with a version we support. */ + std::unique_ptr m_Protocol; + + /** Buffer for received protocol data. */ + cByteBuffer m_Buffer; - /** Sends a single packet contained within the cPacketizer class. - The cPacketizer's destructor calls this to send the contained packet; protocol may transform the data (compression in 1.8 etc). */ - virtual void SendPacket(cPacketizer & a_Pkt) override; } ; diff --git a/src/Protocol/Protocol_1_11.cpp b/src/Protocol/Protocol_1_11.cpp index 204371e90..84151b4a9 100644 --- a/src/Protocol/Protocol_1_11.cpp +++ b/src/Protocol/Protocol_1_11.cpp @@ -619,7 +619,7 @@ void cProtocol_1_11_0::HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer) // Version: Json::Value Version; Version["name"] = "Cuberite 1.11"; - Version["protocol"] = cProtocolRecognizer::PROTO_VERSION_1_11_0; + Version["protocol"] = cMultiVersionProtocol::PROTO_VERSION_1_11_0; // Players: Json::Value Players; @@ -1241,7 +1241,7 @@ void cProtocol_1_11_1::HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer) // Version: Json::Value Version; Version["name"] = "Cuberite 1.11.1"; - Version["protocol"] = cProtocolRecognizer::PROTO_VERSION_1_11_1; + Version["protocol"] = cMultiVersionProtocol::PROTO_VERSION_1_11_1; // Players: Json::Value Players; diff --git a/src/Protocol/Protocol_1_12.cpp b/src/Protocol/Protocol_1_12.cpp index 6998f73bf..0402cdaa3 100644 --- a/src/Protocol/Protocol_1_12.cpp +++ b/src/Protocol/Protocol_1_12.cpp @@ -339,7 +339,7 @@ void cProtocol_1_12::HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer) // Version: Json::Value Version; Version["name"] = "Cuberite 1.12"; - Version["protocol"] = cProtocolRecognizer::PROTO_VERSION_1_12; + Version["protocol"] = cMultiVersionProtocol::PROTO_VERSION_1_12; // Players: Json::Value Players; @@ -1217,7 +1217,7 @@ void cProtocol_1_12_1::HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer) // Version: Json::Value Version; Version["name"] = "Cuberite 1.12.1"; - Version["protocol"] = cProtocolRecognizer::PROTO_VERSION_1_12_1; + Version["protocol"] = cMultiVersionProtocol::PROTO_VERSION_1_12_1; // Players: Json::Value Players; @@ -1374,7 +1374,7 @@ void cProtocol_1_12_2::HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer) // Version: Json::Value Version; Version["name"] = "Cuberite 1.12.2"; - Version["protocol"] = cProtocolRecognizer::PROTO_VERSION_1_12_2; + Version["protocol"] = cMultiVersionProtocol::PROTO_VERSION_1_12_2; // Players: Json::Value Players; diff --git a/src/Protocol/Protocol_1_13.cpp b/src/Protocol/Protocol_1_13.cpp index 1dcecaa4b..996111c4d 100644 --- a/src/Protocol/Protocol_1_13.cpp +++ b/src/Protocol/Protocol_1_13.cpp @@ -231,7 +231,7 @@ void cProtocol_1_13::HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer) // Version: Json::Value Version; Version["name"] = "Cuberite 1.13"; - Version["protocol"] = cProtocolRecognizer::PROTO_VERSION_1_13; + Version["protocol"] = cMultiVersionProtocol::PROTO_VERSION_1_13; // Players: Json::Value Players; diff --git a/src/Protocol/Protocol_1_9.h b/src/Protocol/Protocol_1_9.h index 75fff9020..a09317190 100644 --- a/src/Protocol/Protocol_1_9.h +++ b/src/Protocol/Protocol_1_9.h @@ -62,8 +62,6 @@ public: virtual void SendUnleashEntity (const cEntity & a_Entity) override; virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override; - virtual AString GetAuthServerID(void) override { return m_AuthServerID; } - protected: /** The current teleport ID, and whether it has been confirmed by the client */ diff --git a/src/Root.cpp b/src/Root.cpp index b8d4f9db8..a66b893d9 100644 --- a/src/Root.cpp +++ b/src/Root.cpp @@ -931,7 +931,7 @@ bool cRoot::DoWithPlayer(const AString & a_PlayerName, cPlayerListCallback a_Cal AString cRoot::GetProtocolVersionTextFromInt(int a_ProtocolVersion) { - return cProtocolRecognizer::GetVersionTextFromInt(a_ProtocolVersion); + return cMultiVersionProtocol::GetVersionTextFromInt(a_ProtocolVersion); } diff --git a/src/Server.cpp b/src/Server.cpp index 19a08cc65..372437265 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -181,7 +181,7 @@ bool cServer::InitServer(cSettingsRepositoryInterface & a_Settings, bool a_Shoul } LOGINFO("Compatible clients: %s", MCS_CLIENT_VERSIONS); - LOGINFO("Compatible protocol versions %s", MCS_PROTOCOL_VERSIONS); + LOGD("Compatible protocol versions %s", MCS_PROTOCOL_VERSIONS); m_Ports = ReadUpgradeIniPorts(a_Settings, "Server", "Ports", "Port", "PortsIPv6", "25565");