summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/ClientHandle.cpp22
-rw-r--r--src/ClientHandle.h3
-rw-r--r--src/Protocol/ProtocolRecognizer.cpp1198
-rw-r--r--src/Protocol/ProtocolRecognizer.h176
-rw-r--r--src/Protocol/Protocol_1_11.cpp4
-rw-r--r--src/Protocol/Protocol_1_12.cpp6
-rw-r--r--src/Protocol/Protocol_1_13.cpp2
-rw-r--r--src/Protocol/Protocol_1_9.h2
-rw-r--r--src/Root.cpp2
-rw-r--r--src/Server.cpp2
10 files changed, 250 insertions, 1167 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<cProtocolRecognizer>(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<void *>(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<cChunkCoords, cChunkCoordsHash> 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<cProtocol> 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
+
+ // The protocol recogniser succesfully identified, switch mode:
+ HandleIncomingData = [this](cClientHandle &, const std::string_view a_In)
{
- m_Buffer.ResetRead();
- }
+ // 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);
+}
+
+
+
+
- if (!m_InPingForUnrecognizedVersion)
+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,1049 +161,157 @@ 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;
}
- }
-}
-
-
-
-
-
-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);
+ m_Buffer.CommitRead();
+ }
}
-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);
-}
+ 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<UInt32>(Message.size())) + Message.size()
+ );
-
-
-
-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<int, 2> 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);
+ VERIFY(Out.WriteVarInt32(PacketID));
+ VERIFY(Out.WriteVarUTF8String(Message));
+ SendPacket(a_Client, Out);
}
-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();
-}
-
-
-
-
-
-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);
-}
-
-
-
-
-
-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<UInt32>(m_Buffer.GetReadableSpace());
if (!m_Buffer.ReadVarInt(PacketLen))
{
// Not enough bytes for the packet length, keep waiting
- return false;
+ return;
}
- ReadSoFar -= static_cast<UInt32>(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<cProtocol> 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))
- {
- return false;
- }
- if (!m_Buffer.ReadBEUInt16(ServerPort))
+
+ 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.ReadVarInt(NextState))
+
+ if (
+ !m_Buffer.ReadVarInt(ProtocolVersion) ||
+ !m_Buffer.ReadVarUTF8String(ServerAddress) ||
+ !m_Buffer.ReadBEUInt16(ServerPort) ||
+ !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<cProtocol_1_8_0>(&a_Client, ServerAddress, ServerPort, NextState);
+ case PROTO_VERSION_1_9_0: return std::make_unique<cProtocol_1_9_0>(&a_Client, ServerAddress, ServerPort, NextState);
+ case PROTO_VERSION_1_9_1: return std::make_unique<cProtocol_1_9_1>(&a_Client, ServerAddress, ServerPort, NextState);
+ case PROTO_VERSION_1_9_2: return std::make_unique<cProtocol_1_9_2>(&a_Client, ServerAddress, ServerPort, NextState);
+ case PROTO_VERSION_1_9_4: return std::make_unique<cProtocol_1_9_4>(&a_Client, ServerAddress, ServerPort, NextState);
+ case PROTO_VERSION_1_10_0: return std::make_unique<cProtocol_1_10_0>(&a_Client, ServerAddress, ServerPort, NextState);
+ case PROTO_VERSION_1_11_0: return std::make_unique<cProtocol_1_11_0>(&a_Client, ServerAddress, ServerPort, NextState);
+ case PROTO_VERSION_1_11_1: return std::make_unique<cProtocol_1_11_1>(&a_Client, ServerAddress, ServerPort, NextState);
+ case PROTO_VERSION_1_12: return std::make_unique<cProtocol_1_12>(&a_Client, ServerAddress, ServerPort, NextState);
+ case PROTO_VERSION_1_12_1: return std::make_unique<cProtocol_1_12_1>(&a_Client, ServerAddress, ServerPort, NextState);
+ case PROTO_VERSION_1_12_2: return std::make_unique<cProtocol_1_12_2>(&a_Client, ServerAddress, ServerPort, NextState);
+ case PROTO_VERSION_1_13: return std::make_unique<cProtocol_1_13>(&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<UInt32>(m_OutPacketBuffer.GetUsedSpace());
- AString PacketData, CompressedPacket;
- m_OutPacketBuffer.ReadAll(PacketData);
- m_OutPacketBuffer.CommitRead();
+ UInt32 PacketLen = static_cast<UInt32>(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<signed>(Server->GetNumPlayers());
auto MaxPlayers = static_cast<signed>(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;
-
- /** 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<int, 2> 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;
-
- virtual AString GetAuthServerID(void) override;
-
- virtual void SendData(const char * a_Data, size_t a_Size) override;
-
-protected:
-
- /** The recognized protocol */
- std::unique_ptr<cProtocol> m_Protocol;
+ /** Returns if we contain a concrete protocol corresponding to the client's protocol version. */
+ bool VersionRecognitionSuccessful()
+ {
+ return m_Protocol != nullptr;
+ }
- /** Buffer for the incoming data until we recognize the protocol */
- cByteBuffer m_Buffer;
+ /** Convenience overload to enable redirecting sends to the underlying implementation. */
+ auto & operator->()
+ {
+ return m_Protocol;
+ }
+
+ /** The function that's responsible for processing incoming protocol data. */
+ std::function<void(cClientHandle &, std::string_view)> HandleIncomingData;
+
+ /** 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);
- /** Is a server list ping for an unrecognized version currently occuring? */
- bool m_InPingForUnrecognizedVersion;
+private:
+
+ /** 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);
+
+ /** Handles and responds to unsupported clients sending pings. */
+ void HandleIncomingDataInOldPingResponseStage(cClientHandle & a_Client, const std::string_view a_Data);
+
+ /* 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<cProtocol> 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);
+
+ /* Status handler for unrecognised versions. */
+ void HandlePacketStatusRequest(cClientHandle & a_Client, cByteBuffer & a_Out);
- // Packet handlers while in status state (m_InPingForUnrecognizedVersion == true)
- void HandlePacketStatusRequest();
- void HandlePacketStatusPing();
+ /* Ping handler for unrecognised versions. */
+ void HandlePacketStatusPing(cClientHandle & a_Client, cByteBuffer & a_Out);
- /** Tries to recognize protocol based on m_Buffer contents; returns true if recognized */
- bool TryRecognizeProtocol(void);
+ /** The actual protocol implementation.
+ Created when recognition of the client version succeeds with a version we support. */
+ std::unique_ptr<cProtocol> m_Protocol;
- /** 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);
+ /** 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");