From 251c961eea640e18a90aaddc6c787d999dca09dd Mon Sep 17 00:00:00 2001 From: "madmaxoft@gmail.com" Date: Sun, 23 Sep 2012 20:03:26 +0000 Subject: Source files cleanup: Protocol-related files in a separate subfolder git-svn-id: http://mc-server.googlecode.com/svn/trunk@880 0a769ca7-a7f5-676a-18bf-c427514a06d6 --- source/Protocol/Protocol.h | 173 ++++ source/Protocol/Protocol125.cpp | 1391 ++++++++++++++++++++++++++++++++ source/Protocol/Protocol125.h | 140 ++++ source/Protocol/Protocol132.cpp | 769 ++++++++++++++++++ source/Protocol/Protocol132.h | 99 +++ source/Protocol/ProtocolRecognizer.cpp | 542 +++++++++++++ source/Protocol/ProtocolRecognizer.h | 99 +++ 7 files changed, 3213 insertions(+) create mode 100644 source/Protocol/Protocol.h create mode 100644 source/Protocol/Protocol125.cpp create mode 100644 source/Protocol/Protocol125.h create mode 100644 source/Protocol/Protocol132.cpp create mode 100644 source/Protocol/Protocol132.h create mode 100644 source/Protocol/ProtocolRecognizer.cpp create mode 100644 source/Protocol/ProtocolRecognizer.h (limited to 'source/Protocol') diff --git a/source/Protocol/Protocol.h b/source/Protocol/Protocol.h new file mode 100644 index 000000000..af915a2bb --- /dev/null +++ b/source/Protocol/Protocol.h @@ -0,0 +1,173 @@ + +// Protocol.h + +// Interfaces to the cProtocol class representing the generic interface that a protocol +// parser and serializer must implement + + + + + +#pragma once + +#include "../Defines.h" +#include "../Endianness.h" + + + + +class cPlayer; +class cEntity; +class cWindow; +class cInventory; +class cPawn; +class cPickup; +class cMonster; +class cChunkDataSerializer; +class cWorld; + + + + + +class cProtocol +{ +public: + cProtocol(cClientHandle * a_Client) : + m_Client(a_Client) + { + } + + /// Called when client sends some data + virtual void DataReceived(const char * a_Data, int a_Size) = 0; + + // Sending stuff to clients: + virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) = 0; + virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0; + virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) = 0; + virtual void SendChat (const AString & a_Message) = 0; + virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) = 0; + virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) = 0; + virtual void SendDestroyEntity (const cEntity & a_Entity) = 0; + virtual void SendDisconnect (const AString & a_Reason) = 0; + virtual void SendEntHeadLook (const cEntity & a_Entity) = 0; + virtual void SendEntLook (const cEntity & a_Entity) = 0; + virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) = 0; + virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) = 0; + virtual void SendEntRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) = 0; + virtual void SendEntRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) = 0; + virtual void SendGameMode (eGameMode a_GameMode) = 0; + virtual void SendHealth (void) = 0; + virtual void SendInventoryProgress(char a_WindowID, short a_Progressbar, short a_Value) = 0; + virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) = 0; + virtual void SendKeepAlive (int a_PingID) = 0; + virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) = 0; + virtual void SendMetadata (const cEntity & a_Entity) = 0; + virtual void SendPickupSpawn (const cPickup & a_Pickup) = 0; + virtual void SendPlayerAnimation (const cPlayer & a_Player, char a_Animation) = 0; + virtual void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline) = 0; + virtual void SendPlayerMoveLook (void) = 0; + virtual void SendPlayerPosition (void) = 0; + virtual void SendPlayerSpawn (const cPlayer & a_Player) = 0; + virtual void SendRespawn (void) = 0; + virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) = 0; // a_Src coords are Block * 8 + virtual void SendSpawnMob (const cMonster & a_Mob) = 0; + virtual void SendTeleportEntity (const cEntity & a_Entity) = 0; + virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) = 0; + virtual void SendTimeUpdate (Int64 a_WorldTime) = 0; + virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) = 0; + 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) = 0; + virtual void SendWeather (eWeather a_Weather) = 0; + virtual void SendWholeInventory (const cInventory & a_Inventory) = 0; + virtual void SendWholeInventory (const cWindow & a_Window) = 0; + virtual void SendWindowClose (char a_WindowID) = 0; + virtual void SendWindowOpen (char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots) = 0; + + /// Returns the ServerID used for authentication through session.minecraft.net + virtual AString GetAuthServerID(void) = 0; + +protected: + cClientHandle * m_Client; + cCriticalSection m_CSPacket; //< Each SendXYZ() function must acquire this CS in order to send the whole packet at once + + /// A generic data-sending routine, all outgoing packet data needs to be routed through this so that descendants may override it + virtual void SendData(const char * a_Data, int a_Size) = 0; + + /// Called after writing each packet, enables descendants to flush their buffers + virtual void Flush(void) {}; + + // Helpers for writing partial packet data, write using SendData() + void WriteByte(char a_Value) + { + SendData((const char *)&a_Value, 1); + } + + void WriteShort(short a_Value) + { + a_Value = htons(a_Value); + SendData((const char *)&a_Value, 2); + } + + /* + void WriteShort(unsigned short a_Value) + { + a_Value = htons(a_Value); + SendData((const char *)&a_Value, 2); + } + */ + + void WriteInt(int a_Value) + { + a_Value = htonl(a_Value); + SendData((const char *)&a_Value, 4); + } + + void WriteInt(unsigned int a_Value) + { + a_Value = htonl(a_Value); + SendData((const char *)&a_Value, 4); + } + + void WriteInt64 (Int64 a_Value) + { + a_Value = HostToNetwork8(&a_Value); + SendData((const char *)&a_Value, 8); + } + + void WriteFloat (float a_Value) + { + unsigned int val = HostToNetwork4(&a_Value); + SendData((const char *)&val, 4); + } + + void WriteDouble(double a_Value) + { + unsigned long long val = HostToNetwork8(&a_Value); + SendData((const char *)&val, 8); + } + + void WriteString(const AString & a_Value) + { + AString UTF16; + UTF8ToRawBEUTF16(a_Value.c_str(), a_Value.length(), UTF16); + WriteShort((unsigned short)(UTF16.size() / 2)); + SendData(UTF16.data(), UTF16.size()); + } + + void WriteBool(bool a_Value) + { + WriteByte(a_Value ? 1 : 0); + } + + void WriteVectorI(const Vector3i & a_Vector) + { + WriteInt(a_Vector.x); + WriteInt(a_Vector.y); + WriteInt(a_Vector.z); + } +} ; + + + + + diff --git a/source/Protocol/Protocol125.cpp b/source/Protocol/Protocol125.cpp new file mode 100644 index 000000000..dc2e39c16 --- /dev/null +++ b/source/Protocol/Protocol125.cpp @@ -0,0 +1,1391 @@ + +// Protocol125.cpp + +// Implements the cProtocol125 class representing the release 1.2.5 protocol (#29) +/* +Documentation: + - protocol: http://wiki.vg/wiki/index.php?title=Protocol&oldid=2513 + - session handling: http://wiki.vg/wiki/index.php?title=Session&oldid=2262 + - slot format: http://wiki.vg/wiki/index.php?title=Slot_Data&oldid=2152 +*/ + +#include "Globals.h" + +#include "Protocol125.h" + +#include "../cClientHandle.h" +#include "../ChunkDataSerializer.h" +#include "../cEntity.h" +#include "../cMonster.h" +#include "../cPickup.h" +#include "../cPlayer.h" +#include "../cChatColor.h" +#include "../UI/Window.h" +#include "../cRoot.h" +#include "../cServer.h" + + + + + +enum +{ + PACKET_KEEP_ALIVE = 0x00, + PACKET_LOGIN = 0x01, + PACKET_HANDSHAKE = 0x02, + PACKET_CHAT = 0x03, + PACKET_UPDATE_TIME = 0x04, + PACKET_ENTITY_EQUIPMENT = 0x05, + PACKET_USE_ENTITY = 0x07, + PACKET_UPDATE_HEALTH = 0x08, + PACKET_RESPAWN = 0x09, + PACKET_PLAYER_ON_GROUND = 0x0a, + PACKET_PLAYER_POS = 0x0b, + PACKET_PLAYER_LOOK = 0x0c, + PACKET_PLAYER_MOVE_LOOK = 0x0d, + PACKET_BLOCK_DIG = 0x0e, + PACKET_BLOCK_PLACE = 0x0f, + PACKET_SLOT_SELECTED = 0x10, + PACKET_ADD_TO_INV = 0x11, // TODO: Sure this is not Use Bed?? + PACKET_ANIMATION = 0x12, + PACKET_PACKET_ENTITY_ACTION = 0x13, + PACKET_PLAYER_SPAWN = 0x14, + PACKET_PICKUP_SPAWN = 0x15, + PACKET_COLLECT_PICKUP = 0x16, + PACKET_ADD_VEHICLE = 0x17, + PACKET_SPAWN_MOB = 0x18, + PACKET_DESTROY_ENTITY = 0x1d, + PACKET_ENTITY = 0x1e, + PACKET_ENT_REL_MOVE = 0x1f, + PACKET_ENT_LOOK = 0x20, + PACKET_ENT_REL_MOVE_LOOK = 0x21, + PACKET_ENT_TELEPORT = 0x22, + PACKET_ENT_HEAD_LOOK = 0x23, + PACKET_ENT_STATUS = 0x26, + PACKET_METADATA = 0x28, + PACKET_PRE_CHUNK = 0x32, + PACKET_MAP_CHUNK = 0x33, + PACKET_MULTI_BLOCK = 0x34, + PACKET_BLOCK_CHANGE = 0x35, + PACKET_BLOCK_ACTION = 0x36, + PACKET_EXPLOSION = 0x3C, + PACKET_SOUND_EFFECT = 0x3D, + PACKET_CHANGE_GAME_STATE = 0x46, + PACKET_THUNDERBOLT = 0x47, + PACKET_WINDOW_OPEN = 0x64, + PACKET_WINDOW_CLOSE = 0x65, + PACKET_WINDOW_CLICK = 0x66, + PACKET_INVENTORY_SLOT = 0x67, + PACKET_INVENTORY_WHOLE = 0x68, + PACKET_INVENTORY_PROGRESS = 0x69, + PACKET_CREATIVE_INVENTORY_ACTION = 0x6B, + PACKET_UPDATE_SIGN = 0x82, + PACKET_PLAYER_LIST_ITEM = 0xC9, + PACKET_PLAYER_ABILITIES = 0xca, + PACKET_PING = 0xfe, + PACKET_DISCONNECT = 0xff +} ; + + + + + +#define HANDLE_PACKET_READ(Proc, Type, Var) \ + Type Var; \ + { \ + if (!m_ReceivedData.Proc(Var)) \ + { \ + return PARSE_INCOMPLETE; \ + } \ + } + + + + +typedef unsigned char Byte; + + + + + +cProtocol125::cProtocol125(cClientHandle * a_Client) : + super(a_Client), + m_ReceivedData(64 KiB) +{ +} + + + + + +void cProtocol125::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) +{ + UNUSED(a_BlockType); + + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_BLOCK_ACTION); + WriteInt (a_BlockX); + WriteShort((short)a_BlockY); + WriteInt (a_BlockZ); + WriteByte (a_Byte1); + WriteByte (a_Byte2); + Flush(); +} + + + + + +void cProtocol125::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_BLOCK_CHANGE); + WriteInt (a_BlockX); + WriteByte((unsigned char)a_BlockY); + WriteInt (a_BlockZ); + WriteByte(a_BlockType); + WriteByte(a_BlockMeta); + Flush(); +} + + + + + +void cProtocol125::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) +{ + cCSLock Lock(m_CSPacket); + if (a_Changes.size() == 1) + { + // Special packet for single-block changes + const sSetBlock & blk = a_Changes.front(); + SendBlockChange(a_ChunkX * cChunkDef::Width + blk.x, blk.y, a_ChunkZ * cChunkDef::Width + blk.z, blk.BlockType, blk.BlockMeta); + return; + } + + WriteByte (PACKET_MULTI_BLOCK); + WriteInt (a_ChunkX); + WriteInt (a_ChunkZ); + WriteShort((unsigned short)a_Changes.size()); + WriteInt (sizeof(int) * a_Changes.size()); + for (sSetBlockVector::const_iterator itr = a_Changes.begin(), end = a_Changes.end(); itr != end; ++itr) + { + unsigned int Coords = itr->y | (itr->z << 8) | (itr->x << 12); + unsigned int Blocks = itr->BlockMeta | (itr->BlockType << 4); + WriteInt(Coords << 16 | Blocks); + } + Flush(); +} + + + + + +void cProtocol125::SendChat(const AString & a_Message) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_CHAT); + WriteString(a_Message); + Flush(); +} + + + + + +void cProtocol125::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) +{ + cCSLock Lock(m_CSPacket); + + // Send the pre-chunk: + SendPreChunk(a_ChunkX, a_ChunkZ, true); + + // Send the chunk data: + AString Serialized = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_2_5); + WriteByte(PACKET_MAP_CHUNK); + WriteInt (a_ChunkX); + WriteInt (a_ChunkZ); + SendData(Serialized.data(), Serialized.size()); + Flush(); +} + + + + + +void cProtocol125::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_COLLECT_PICKUP); + WriteInt (a_Pickup.GetUniqueID()); + WriteInt (a_Player.GetUniqueID()); + Flush(); +} + + + + + +void cProtocol125::SendDestroyEntity(const cEntity & a_Entity) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_DESTROY_ENTITY); + WriteInt (a_Entity.GetUniqueID()); + Flush(); +} + + + + + +void cProtocol125::SendDisconnect(const AString & a_Reason) +{ + cCSLock Lock(m_CSPacket); + WriteByte ((unsigned char)PACKET_DISCONNECT); + WriteString(a_Reason); + Flush(); +} + + + + + +void cProtocol125::SendEntHeadLook(const cEntity & a_Entity) +{ + ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self + + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_ENT_HEAD_LOOK); + WriteInt (a_Entity.GetUniqueID()); + WriteByte((char)((a_Entity.GetRotation() / 360.f) * 256)); + Flush(); +} + + + + + +void cProtocol125::SendEntLook(const cEntity & a_Entity) +{ + ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self + + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_ENT_LOOK); + WriteInt (a_Entity.GetUniqueID()); + WriteByte((char)((a_Entity.GetRotation() / 360.f) * 256)); + WriteByte((char)((a_Entity.GetPitch() / 360.f) * 256)); + Flush(); +} + + + + + +void cProtocol125::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_ENTITY_EQUIPMENT); + WriteInt (a_Entity.GetUniqueID()); + WriteShort(a_SlotNum); + WriteShort(a_Item.m_ItemType); + WriteShort(a_Item.m_ItemDamage); + Flush(); +} + + + + + +void cProtocol125::SendEntityStatus(const cEntity & a_Entity, char a_Status) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_ENT_STATUS); + WriteInt (a_Entity.GetUniqueID()); + WriteByte(a_Status); + Flush(); +} + + + + + +void cProtocol125::SendEntRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) +{ + ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self + + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_ENT_REL_MOVE); + WriteInt (a_Entity.GetUniqueID()); + WriteByte(a_RelX); + WriteByte(a_RelY); + WriteByte(a_RelZ); + Flush(); +} + + + + + +void cProtocol125::SendEntRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) +{ + ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self + + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_ENT_REL_MOVE_LOOK); + WriteInt (a_Entity.GetUniqueID()); + WriteByte(a_RelX); + WriteByte(a_RelY); + WriteByte(a_RelZ); + WriteByte((char)((a_Entity.GetRotation() / 360.f) * 256)); + WriteByte((char)((a_Entity.GetPitch() / 360.f) * 256)); + Flush(); +} + + + + + +void cProtocol125::SendGameMode(eGameMode a_GameMode) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_CHANGE_GAME_STATE); + WriteByte(3); + WriteByte((char)a_GameMode); + Flush(); +} + + + + + +void cProtocol125::SendHandshake(const AString & a_ConnectionHash) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_HANDSHAKE); + WriteString(a_ConnectionHash); + Flush(); +} + + + + + +void cProtocol125::SendHealth(void) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_UPDATE_HEALTH); + WriteShort((short)m_Client->GetPlayer()->GetHealth()); + WriteShort(m_Client->GetPlayer()->GetFoodLevel()); + WriteFloat(m_Client->GetPlayer()->GetFoodSaturationLevel()); + Flush(); +} + + + + + +void cProtocol125::SendInventoryProgress(char a_WindowID, short a_ProgressBar, short a_Value) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_INVENTORY_PROGRESS); + WriteByte (a_WindowID); + WriteShort(a_ProgressBar); + WriteShort(a_Value); + Flush(); +} + + + + + +void cProtocol125::SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem & a_Item) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_INVENTORY_SLOT); + WriteByte (a_WindowID); + WriteShort(a_SlotNum); + WriteItem (a_Item); + Flush(); +} + + + + + +void cProtocol125::SendKeepAlive(int a_PingID) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_KEEP_ALIVE); + WriteInt (a_PingID); +} + + + + + +void cProtocol125::SendLogin(const cPlayer & a_Player, const cWorld & a_World) +{ + UNUSED(a_World); + cCSLock Lock(m_CSPacket); + + WriteByte (PACKET_LOGIN); + WriteInt (a_Player.GetUniqueID()); // EntityID of the player + WriteString(""); // Username, not used + WriteString("default"); // Level type + WriteInt ((int)a_Player.GetGameMode()); + WriteInt (0); // TODO: Dimension (Nether / Overworld / End) + WriteByte (2); // TODO: Difficulty + WriteByte (0); // Unused + WriteByte (60); // Client list width or something + Flush(); +} + + + + + +void cProtocol125::SendMetadata(const cEntity & a_Entity) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_METADATA); + WriteInt (a_Entity.GetUniqueID()); + AString MetaData = GetEntityMetaData(a_Entity); + SendData(MetaData.data(), MetaData.size()); + Flush(); +} + + + + + +void cProtocol125::SendPickupSpawn(const cPickup & a_Pickup) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_PICKUP_SPAWN); + WriteInt (a_Pickup.GetUniqueID()); + WriteShort (a_Pickup.GetItem()->m_ItemType); + WriteByte (a_Pickup.GetItem()->m_ItemCount); + WriteShort (a_Pickup.GetItem()->m_ItemDamage); + WriteVectorI((Vector3i)(a_Pickup.GetPosition() * 32)); + WriteByte ((char)(a_Pickup.GetSpeed().x * 8)); + WriteByte ((char)(a_Pickup.GetSpeed().y * 8)); + WriteByte ((char)(a_Pickup.GetSpeed().z * 8)); + Flush(); +} + + + + + +void cProtocol125::SendPlayerAnimation(const cPlayer & a_Player, char a_Animation) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_ANIMATION); + WriteInt (a_Player.GetUniqueID()); + WriteByte(a_Animation); + Flush(); +} + + + + + +void cProtocol125::SendPlayerListItem(const cPlayer & a_Player, bool a_IsOnline) +{ + cCSLock Lock(m_CSPacket); + AString PlayerName(a_Player.GetColor()); + PlayerName.append(a_Player.GetName()); + if (PlayerName.length() > 14) + { + PlayerName.erase(14); + } + PlayerName += cChatColor::White; + + WriteByte ((unsigned char)PACKET_PLAYER_LIST_ITEM); + WriteString(PlayerName); + WriteBool (a_IsOnline); + WriteShort (a_Player.GetClientHandle()->GetPing()); + Flush(); +} + + + + + +void cProtocol125::SendPlayerMoveLook(void) +{ + cCSLock Lock(m_CSPacket); + + /* + LOGD("Sending PlayerMoveLook: {%0.2f, %0.2f, %0.2f}, stance %0.2f, OnGround: %d", + m_Player->GetPosX(), m_Player->GetPosY(), m_Player->GetPosZ(), m_Player->GetStance(), m_Player->IsOnGround() ? 1 : 0 + ); + */ + + WriteByte (PACKET_PLAYER_MOVE_LOOK); + cPlayer * Player = m_Client->GetPlayer(); + WriteDouble(Player->GetPosX()); + WriteDouble(Player->GetStance() + 0.03); // Add a small amount so that the player doesn't start inside a block + WriteDouble(Player->GetPosY() + 0.03); // Add a small amount so that the player doesn't start inside a block + WriteDouble(Player->GetPosZ()); + WriteFloat (Player->GetRotation()); + WriteFloat (Player->GetPitch()); + WriteBool (Player->IsOnGround()); + Flush(); +} + + + + + +void cProtocol125::SendPlayerPosition(void) +{ + cCSLock Lock(m_CSPacket); + LOGD("Ignore send PlayerPos"); // PlayerPos is a C->S packet only now +} + + + + + +void cProtocol125::SendPlayerSpawn(const cPlayer & a_Player) +{ + const cItem & HeldItem = a_Player.GetEquippedItem(); + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_PLAYER_SPAWN); + WriteInt (a_Player.GetUniqueID()); + WriteString(a_Player.GetName()); + WriteInt ((int)(a_Player.GetPosX() * 32)); + WriteInt ((int)(a_Player.GetPosY() * 32)); + WriteInt ((int)(a_Player.GetPosZ() * 32)); + WriteByte ((char)((a_Player.GetRot().x / 360.f) * 256)); + WriteByte ((char)((a_Player.GetRot().y / 360.f) * 256)); + WriteShort (HeldItem.IsEmpty() ? 0 : HeldItem.m_ItemType); + Flush(); +} + + + + + +void cProtocol125::SendRespawn(void) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_RESPAWN); + WriteInt (0); // TODO: Dimension; 0 = Overworld + WriteByte (2); // TODO: Difficulty; 2 = Normal + WriteByte ((char)m_Client->GetPlayer()->GetGameMode()); + WriteShort (256); // Current world height + WriteString("default"); +} + + + + + +void cProtocol125::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) +{ + cCSLock Lock(m_CSPacket); + //TODO: Not needed in this protocol? +} + + + + + +void cProtocol125::SendSpawnMob(const cMonster & a_Mob) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_SPAWN_MOB); + WriteInt (a_Mob.GetUniqueID()); + WriteByte (a_Mob.GetMobType()); + WriteVectorI((Vector3i)(a_Mob.GetPosition() * 32)); + WriteByte (0); + WriteByte (0); + WriteByte (0); + AString MetaData = GetEntityMetaData(a_Mob); + SendData (MetaData.data(), MetaData.size()); + Flush(); +} + + + + + +void cProtocol125::SendTeleportEntity(const cEntity & a_Entity) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_ENT_TELEPORT); + WriteInt (a_Entity.GetUniqueID()); + WriteVectorI((Vector3i)(a_Entity.GetPosition() * 32)); + WriteByte ((char)((a_Entity.GetRotation() / 360.f) * 256)); + WriteByte ((char)((a_Entity.GetPitch() / 360.f) * 256)); + Flush(); +} + + + + + +void cProtocol125::SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_THUNDERBOLT); + WriteInt (0x7fffffff); // Entity ID of the thunderbolt; we use a constant one + WriteBool(true); // Unknown bool + WriteInt (a_BlockX * 32); + WriteInt (a_BlockY * 32); + WriteInt (a_BlockZ * 32); + Flush(); +} + + + + + +void cProtocol125::SendTimeUpdate(Int64 a_WorldTime) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_UPDATE_TIME); + WriteInt64(a_WorldTime); + Flush(); +} + + + + + +void cProtocol125::SendUnloadChunk(int a_ChunkX, int a_ChunkZ) +{ + cCSLock Lock(m_CSPacket); + SendPreChunk(a_ChunkX, a_ChunkZ, false); +} + + + + + +void cProtocol125::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 +) +{ + cCSLock Lock(m_CSPacket); + WriteByte ((unsigned char)PACKET_UPDATE_SIGN); + WriteInt (a_BlockX); + WriteShort ((short)a_BlockY); + WriteInt (a_BlockZ); + WriteString(a_Line1); + WriteString(a_Line2); + WriteString(a_Line3); + WriteString(a_Line4); + Flush(); +} + + + + + +void cProtocol125::SendWeather(eWeather a_Weather) +{ + cCSLock Lock(m_CSPacket); + switch( a_Weather ) + { + case eWeather_Sunny: + { + WriteByte(PACKET_CHANGE_GAME_STATE); + WriteByte(2); // Stop rain + WriteByte(0); // Unused + Flush(); + break; + } + + case eWeather_Rain: + case eWeather_ThunderStorm: + { + WriteByte(PACKET_CHANGE_GAME_STATE); + WriteByte(1); // Begin rain + WriteByte(0); // Unused + Flush(); + break; + } + } +} + + + + + +void cProtocol125::SendWholeInventory(const cInventory & a_Inventory) +{ + cCSLock Lock(m_CSPacket); + SendWindowSlots(0, a_Inventory.c_NumSlots, a_Inventory.GetSlots()); +} + + + + + +void cProtocol125::SendWholeInventory(const cWindow & a_Window) +{ + cCSLock Lock(m_CSPacket); + cItems Slots; + a_Window.GetSlots(*(m_Client->GetPlayer()), Slots); + SendWindowSlots(a_Window.GetWindowID(), Slots.size(), &(Slots[0])); +} + + + + + +void cProtocol125::SendWindowClose(char a_WindowID) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_WINDOW_CLOSE); + WriteByte(a_WindowID); + Flush(); +} + + + + + +void cProtocol125::SendWindowOpen(char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots) +{ + if (a_WindowType < 0) + { + // Do not send for inventory windows + return; + } + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_WINDOW_OPEN); + WriteByte (a_WindowID); + WriteByte (a_WindowType); + WriteString(a_WindowTitle); + WriteByte (a_NumSlots); + Flush(); +} + + + + + +AString cProtocol125::GetAuthServerID(void) +{ + // http://wiki.vg/wiki/index.php?title=Session&oldid=2262 + // The server generates a random hash and that is used for all clients, unmodified + return cRoot::Get()->GetServer()->GetServerID(); +} + + + + + +void cProtocol125::SendData(const char * a_Data, int a_Size) +{ + m_Client->SendData(a_Data, a_Size); +} + + + + + +void cProtocol125::DataReceived(const char * a_Data, int a_Size) +{ + if (!m_ReceivedData.Write(a_Data, a_Size)) + { + // Too much data in the incoming queue, report to caller: + m_Client->PacketBufferFull(); + return; + } + + // Parse and handle all complete packets in m_ReceivedData: + while (m_ReceivedData.CanReadBytes(1)) + { + unsigned char PacketType; + m_ReceivedData.ReadByte(PacketType); + switch (ParsePacket(PacketType)) + { + case PARSE_UNKNOWN: + { + // An unknown packet has been received, notify the client and abort: + m_Client->PacketUnknown(PacketType); + return; + } + case PARSE_ERROR: + { + // An error occurred while parsing a known packet, notify the client and abort: + m_Client->PacketError(PacketType); + return; + } + case PARSE_INCOMPLETE: + { + // Incomplete packet, bail out and process with the next batch of data + m_ReceivedData.ResetRead(); + return; + } + default: + { + // Packet successfully parsed, commit the read data and try again one more packet + m_ReceivedData.CommitRead(); + break; + } + } + } +} + + + + + +int cProtocol125::ParsePacket(unsigned char a_PacketType) +{ + switch (a_PacketType) + { + default: return PARSE_UNKNOWN; + case PACKET_ANIMATION: return ParseArmAnim(); + case PACKET_BLOCK_DIG: return ParseBlockDig(); + case PACKET_BLOCK_PLACE: return ParseBlockPlace(); + case PACKET_CHAT: return ParseChat(); + case PACKET_CREATIVE_INVENTORY_ACTION: return ParseCreativeInventoryAction(); + case PACKET_DISCONNECT: return ParseDisconnect(); + case PACKET_HANDSHAKE: return ParseHandshake(); + case PACKET_KEEP_ALIVE: return ParseKeepAlive(); + case PACKET_LOGIN: return ParseLogin(); + case PACKET_PACKET_ENTITY_ACTION: return ParseEntityAction(); + case PACKET_PING: return ParsePing(); + case PACKET_PLAYER_ABILITIES: return ParsePlayerAbilities(); + case PACKET_PLAYER_LOOK: return ParsePlayerLook(); + case PACKET_PLAYER_MOVE_LOOK: return ParsePlayerMoveLook(); + case PACKET_PLAYER_ON_GROUND: return ParsePlayerOnGround(); + case PACKET_PLAYER_POS: return ParsePlayerPosition(); + case PACKET_RESPAWN: return ParseRespawn(); + case PACKET_SLOT_SELECTED: return ParseSlotSelected(); + case PACKET_UPDATE_SIGN: return ParseUpdateSign(); + case PACKET_USE_ENTITY: return ParseUseEntity(); + case PACKET_WINDOW_CLICK: return ParseWindowClick(); + case PACKET_WINDOW_CLOSE: return ParseWindowClose(); + } +} + + + + + +#define HANDLE_PACKET_PARSE(Packet) \ + { \ + int res = Packet.Parse(m_ReceivedData); \ + if (res < 0) \ + { \ + return res; \ + } \ + } + + + + + +int cProtocol125::ParseArmAnim(void) +{ + HANDLE_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_PACKET_READ(ReadChar, char, Animation); + m_Client->HandleAnimation(Animation); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseBlockDig(void) +{ + HANDLE_PACKET_READ(ReadChar, char, Status); + HANDLE_PACKET_READ(ReadBEInt, int, PosX); + HANDLE_PACKET_READ(ReadByte, Byte, PosY); + HANDLE_PACKET_READ(ReadBEInt, int, PosZ); + HANDLE_PACKET_READ(ReadChar, char, Direction); + m_Client->HandleBlockDig(PosX, PosY, PosZ, Direction, Status); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseBlockPlace(void) +{ + HANDLE_PACKET_READ(ReadBEInt, int, PosX); + HANDLE_PACKET_READ(ReadByte, Byte, PosY); + HANDLE_PACKET_READ(ReadBEInt, int, PosZ); + HANDLE_PACKET_READ(ReadChar, char, Direction); + + cItem HeldItem; + int res = ParseItem(HeldItem); + if (res < 0) + { + return res; + } + + m_Client->HandleBlockPlace(PosX, PosY, PosZ, Direction, HeldItem); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseChat(void) +{ + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Message); + m_Client->HandleChat(Message); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseCreativeInventoryAction(void) +{ + HANDLE_PACKET_READ(ReadBEShort, short, SlotNum); + cItem HeldItem; + int res = ParseItem(HeldItem); + if (res < 0) + { + return res; + } + m_Client->HandleCreativeInventory(SlotNum, HeldItem); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseDisconnect(void) +{ + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Reason); + m_Client->HandleDisconnect(Reason); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseEntityAction(void) +{ + HANDLE_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_PACKET_READ(ReadChar, char, ActionID); + // TODO: m_Client->HandleEntityAction(...); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseHandshake(void) +{ + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Username); + + AStringVector UserData = StringSplit(Username, ";"); // "FakeTruth;localhost:25565" + if (UserData.empty()) + { + m_Client->Kick("Did not receive username"); + return PARSE_OK; + } + m_Username = UserData[0]; + + LOGD("HANDSHAKE %s", Username.c_str()); + + if (!m_Client->HandleHandshake( m_Username )) + { + return PARSE_OK; // Player is not allowed into the server + } + + SendHandshake(cRoot::Get()->GetServer()->GetServerID()); + LOGD("User \"%s\" was sent a handshake response", m_Username.c_str()); + + return PARSE_OK; +} + + + + + +int cProtocol125::ParseKeepAlive(void) +{ + HANDLE_PACKET_READ(ReadBEInt, int, KeepAliveID); + m_Client->HandleKeepAlive(KeepAliveID); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseLogin(void) +{ + HANDLE_PACKET_READ(ReadBEInt, int, ProtocolVersion); + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Username); + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, LevelType); + HANDLE_PACKET_READ(ReadBEInt, int, ServerMode); + HANDLE_PACKET_READ(ReadBEInt, int, Dimension); + HANDLE_PACKET_READ(ReadChar, char, Difficulty); + HANDLE_PACKET_READ(ReadByte, Byte, WorldHeight); + HANDLE_PACKET_READ(ReadByte, Byte, MaxPlayers); + + if (ProtocolVersion < 29) + { + m_Client->Kick("Your client is outdated!"); + return PARSE_OK; + } + else if (ProtocolVersion > 29) + { + m_Client->Kick("Your client version is higher than the server!"); + return PARSE_OK; + } + + if (m_Username.compare(Username) != 0) + { + LOGWARNING("Login Username (\"%s\") does not match Handshake username (\"%s\") for client @ \"%s\", kicking", + Username.c_str(), + m_Username.c_str(), + m_Client->GetIPString().c_str() + ); + m_Client->Kick("Hacked client"); // Don't tell them why we don't want them + return PARSE_OK; + } + + m_Client->HandleLogin(ProtocolVersion, Username); + return PARSE_OK; +} + + + + + +int cProtocol125::ParsePing(void) +{ + // Packet has no more data + m_Client->HandlePing(); + return PARSE_OK; +} + + + + + + +int cProtocol125::ParsePlayerAbilities(void) +{ + HANDLE_PACKET_READ(ReadBool, bool, Invulnerable); + HANDLE_PACKET_READ(ReadBool, bool, IsFlying); + HANDLE_PACKET_READ(ReadBool, bool, CanFly); + HANDLE_PACKET_READ(ReadBool, bool, InstaMine); + // TODO: m_Client->HandlePlayerAbilities(...); + return PARSE_OK; +} + + + + + +int cProtocol125::ParsePlayerLook(void) +{ + HANDLE_PACKET_READ(ReadBEFloat, float, Rotation); + HANDLE_PACKET_READ(ReadBEFloat, float, Pitch); + HANDLE_PACKET_READ(ReadBool, bool, IsOnGround); + m_Client->HandlePlayerLook(Rotation, Pitch, IsOnGround); + return PARSE_OK; +} + + + + + +int cProtocol125::ParsePlayerMoveLook(void) +{ + HANDLE_PACKET_READ(ReadBEDouble, double, PosX); + HANDLE_PACKET_READ(ReadBEDouble, double, PosY); + HANDLE_PACKET_READ(ReadBEDouble, double, Stance); + HANDLE_PACKET_READ(ReadBEDouble, double, PosZ); + HANDLE_PACKET_READ(ReadBEFloat, float, Rotation); + HANDLE_PACKET_READ(ReadBEFloat, float, Pitch); + HANDLE_PACKET_READ(ReadBool, bool, IsOnGround); + // LOGD("Recv PML: {%0.2f, %0.2f, %0.2f}, Stance %0.2f, Gnd: %d", PosX, PosY, PosZ, Stance, IsOnGround ? 1 : 0); + m_Client->HandlePlayerMoveLook(PosX, PosY, PosZ, Stance, Rotation, Pitch, IsOnGround); + return PARSE_OK; +} + + + + + +int cProtocol125::ParsePlayerOnGround(void) +{ + HANDLE_PACKET_READ(ReadBool, bool, IsOnGround); + // TODO: m_Client->HandleFlying(IsOnGround); + return PARSE_OK; +} + + + + + +int cProtocol125::ParsePlayerPosition(void) +{ + HANDLE_PACKET_READ(ReadBEDouble, double, PosX); + HANDLE_PACKET_READ(ReadBEDouble, double, PosY); + HANDLE_PACKET_READ(ReadBEDouble, double, Stance); + HANDLE_PACKET_READ(ReadBEDouble, double, PosZ); + HANDLE_PACKET_READ(ReadBool, bool, IsOnGround); + m_Client->HandlePlayerPos(PosX, PosY, PosZ, Stance, IsOnGround); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseRespawn(void) +{ + HANDLE_PACKET_READ(ReadBEInt, int, Dimension); + HANDLE_PACKET_READ(ReadChar, char, Difficulty); + HANDLE_PACKET_READ(ReadChar, char, CreativeMode); + HANDLE_PACKET_READ(ReadBEShort, short, WorldHeight); + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, LevelType); + m_Client->HandleRespawn(); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseSlotSelected(void) +{ + HANDLE_PACKET_READ(ReadBEShort, short, SlotNum); + m_Client->HandleSlotSelected(SlotNum); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseUpdateSign(void) +{ + HANDLE_PACKET_READ(ReadBEInt, int, BlockX); + HANDLE_PACKET_READ(ReadBEShort, short, BlockY); + HANDLE_PACKET_READ(ReadBEInt, int, BlockZ); + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Line1); + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Line2); + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Line3); + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Line4); + m_Client->HandleUpdateSign(BlockX, BlockY, BlockZ, Line1, Line2, Line3, Line4); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseUseEntity(void) +{ + HANDLE_PACKET_READ(ReadBEInt, int, SourceEntityID); + HANDLE_PACKET_READ(ReadBEInt, int, TargetEntityID); + HANDLE_PACKET_READ(ReadBool, bool, IsLeftClick); + m_Client->HandleUseEntity(TargetEntityID, IsLeftClick); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseWindowClick(void) +{ + HANDLE_PACKET_READ(ReadChar, char, WindowID); + HANDLE_PACKET_READ(ReadBEShort, short, SlotNum); + HANDLE_PACKET_READ(ReadBool, bool, IsRightClick); + HANDLE_PACKET_READ(ReadBEShort, short, TransactionID); + HANDLE_PACKET_READ(ReadBool, bool, IsShiftPressed); + cItem HeldItem; + int res = ParseItem(HeldItem); + if (res < 0) + { + return res; + } + m_Client->HandleWindowClick(WindowID, SlotNum, IsRightClick, IsShiftPressed, HeldItem); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseWindowClose(void) +{ + HANDLE_PACKET_READ(ReadChar, char, WindowID); + m_Client->HandleWindowClose(WindowID); + return PARSE_OK; +} + + + + + +void cProtocol125::SendPreChunk(int a_ChunkX, int a_ChunkZ, bool a_ShouldLoad) +{ + WriteByte(PACKET_PRE_CHUNK); + WriteInt (a_ChunkX); + WriteInt (a_ChunkZ); + WriteBool(a_ShouldLoad); + Flush(); +} + + + + + +void cProtocol125::SendWindowSlots(char a_WindowID, int a_NumItems, const cItem * a_Items) +{ + WriteByte (PACKET_INVENTORY_WHOLE); + WriteByte (a_WindowID); + WriteShort((short)a_NumItems); + + for (int j = 0; j < a_NumItems; j++) + { + WriteItem(a_Items[j]); + } + Flush(); +} + + + + + +void cProtocol125::WriteItem(const cItem & a_Item) +{ + short ItemType = a_Item.m_ItemType; + ASSERT(ItemType >= -1); // Check validity of packets in debug runtime + if (ItemType <= 0) + { + // Fix, to make sure no invalid values are sent. + ItemType = -1; + } + + WriteShort(ItemType); + if (a_Item.IsEmpty()) + { + return; + } + + WriteByte (a_Item.m_ItemCount); + WriteShort(a_Item.m_ItemDamage); + + if (cItem::IsEnchantable(a_Item.m_ItemType)) + { + // TODO: Implement enchantments + WriteShort(-1); + } +} + + + + + +int cProtocol125::ParseItem(cItem & a_Item) +{ + HANDLE_PACKET_READ(ReadBEShort, short, ItemType); + + if (ItemType <= -1) + { + a_Item.Empty(); + return PARSE_OK; + } + a_Item.m_ItemType = ItemType; + + HANDLE_PACKET_READ(ReadChar, char, ItemCount); + HANDLE_PACKET_READ(ReadBEShort, short, ItemDamage); + a_Item.m_ItemCount = ItemCount; + a_Item.m_ItemDamage = ItemDamage; + if (ItemCount <= 0) + { + a_Item.Empty(); + } + + if (!cItem::IsEnchantable(ItemType)) + { + return PARSE_OK; + } + + HANDLE_PACKET_READ(ReadBEShort, short, EnchantNumBytes); + + if (EnchantNumBytes <= 0) + { + return PARSE_OK; + } + + // TODO: Enchantment not implemented yet! + if (!m_ReceivedData.SkipRead(EnchantNumBytes)) + { + return PARSE_INCOMPLETE; + } + + return PARSE_OK; +} + + + + + +AString cProtocol125::GetEntityMetaData(const cEntity & a_Entity) +{ + // We should send all the metadata here + AString MetaData; + // Common metadata (index 0, byte): + MetaData.push_back(0); + MetaData.push_back(GetEntityMetadataFlags(a_Entity)); + + // TODO: Add more entity-specific metadata + + MetaData.push_back(0x7f); // End metadata + return MetaData; +} + + + + + +char cProtocol125::GetEntityMetadataFlags(const cEntity & a_Entity) +{ + char Flags = 0; + if (a_Entity.IsOnFire()) + { + Flags |= 1; + } + if (a_Entity.IsCrouched()) + { + Flags |= 2; + } + if (a_Entity.IsRiding()) + { + Flags |= 4; + } + if (a_Entity.IsSprinting()) + { + Flags |= 8; + } + if (a_Entity.IsRclking()) + { + Flags |= 16; + } + return Flags; +} + + + + diff --git a/source/Protocol/Protocol125.h b/source/Protocol/Protocol125.h new file mode 100644 index 000000000..62c91c2e1 --- /dev/null +++ b/source/Protocol/Protocol125.h @@ -0,0 +1,140 @@ + +// Protocol125.h + +// Interfaces to the cProtocol125 class representing the release 1.2.5 protocol (#29) + + + + + +#pragma once + +#include "Protocol.h" +#include "../ByteBuffer.h" + + + + + +class cProtocol125 : + public cProtocol +{ + typedef cProtocol super; +public: + cProtocol125(cClientHandle * a_Client); + + /// Called when client sends some data: + virtual void DataReceived(const char * a_Data, int a_Size) override; + + /// Sending stuff to clients: + virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) 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 SendChat (const AString & a_Message) override; + virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override; + virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override; + virtual void SendDestroyEntity (const cEntity & a_Entity) override; + virtual void SendDisconnect (const AString & a_Reason) override; + virtual void SendEntHeadLook (const cEntity & a_Entity) override; + virtual void SendEntLook (const cEntity & a_Entity) override; + virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override; + virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) override; + virtual void SendEntRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override; + virtual void SendEntRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override; + virtual void SendGameMode (eGameMode a_GameMode) override; + virtual void SendHealth (void) override; + virtual void SendInventoryProgress(char a_WindowID, short a_Progressbar, short a_Value) override; + virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override; + virtual void SendKeepAlive (int a_PingID) override; + virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override; + virtual void SendMetadata (const cEntity & a_Entity) override; + virtual void SendPickupSpawn (const cPickup & a_Pickup) override; + virtual void SendPlayerAnimation (const cPlayer & a_Player, char a_Animation) override; + virtual void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline) override; + virtual void SendPlayerMoveLook (void) override; + virtual void SendPlayerPosition (void) override; + virtual void SendPlayerSpawn (const cPlayer & a_Player) override; + virtual void SendRespawn (void) override; + virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; // a_Src coords are Block * 8 + virtual void SendSpawnMob (const cMonster & a_Mob) override; + virtual void SendTeleportEntity (const cEntity & a_Entity) override; + virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override; + virtual void SendTimeUpdate (Int64 a_WorldTime) override; + virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) 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 SendWeather (eWeather a_Weather) override; + virtual void SendWholeInventory (const cInventory & a_Inventory) override; + virtual void SendWholeInventory (const cWindow & a_Window) override; + virtual void SendWindowClose (char a_WindowID) override; + virtual void SendWindowOpen (char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots) override; + + virtual AString GetAuthServerID(void) override; + +protected: + /// Results of packet-parsing: + enum { + PARSE_OK = 1, + PARSE_ERROR = -1, + PARSE_UNKNOWN = -2, + PARSE_INCOMPLETE = -3, + } ; + + cByteBuffer m_ReceivedData; //< Buffer for the received data + + AString m_Username; //< Stored in ParseHandshake(), compared to Login username + + virtual void SendData(const char * a_Data, int a_Size) override; + + /// Sends the Handshake packet + void SendHandshake(const AString & a_ConnectionHash); + + /// Parse the packet of the specified type from m_ReceivedData (switch into ParseXYZ() ) + virtual int ParsePacket(unsigned char a_PacketType); + + // Specific packet parsers: + virtual int ParseArmAnim (void); + virtual int ParseBlockDig (void); + virtual int ParseBlockPlace (void); + virtual int ParseChat (void); + virtual int ParseCreativeInventoryAction(void); + virtual int ParseDisconnect (void); + virtual int ParseEntityAction (void); + virtual int ParseHandshake (void); + virtual int ParseKeepAlive (void); + virtual int ParseLogin (void); + virtual int ParsePing (void); + virtual int ParsePlayerAbilities (void); + virtual int ParsePlayerLook (void); + virtual int ParsePlayerMoveLook (void); + virtual int ParsePlayerOnGround (void); + virtual int ParsePlayerPosition (void); + virtual int ParseRespawn (void); + virtual int ParseSlotSelected (void); + virtual int ParseUpdateSign (void); + virtual int ParseUseEntity (void); + virtual int ParseWindowClick (void); + virtual int ParseWindowClose (void); + + // Utility functions: + /// Writes a "pre-chunk" packet + void SendPreChunk(int a_ChunkX, int a_ChunkZ, bool a_ShouldLoad); + + /// Writes a "set window items" packet with the specified params + void SendWindowSlots(char a_WindowID, int a_NumItems, const cItem * a_Items); + + /// Writes one item, "slot" as the protocol wiki calls it + virtual void WriteItem(const cItem & a_Item); + + /// Parses one item, "slot" as the protocol wiki calls it, from m_ReceivedData; returns the usual ParsePacket() codes + virtual int ParseItem(cItem & a_Item); + + /// Returns the entity metadata representation + AString GetEntityMetaData(const cEntity & a_Entity); + + /// Returns the entity common metadata, index 0 (generic flags) + char GetEntityMetadataFlags(const cEntity & a_Entity); +} ; + + + + diff --git a/source/Protocol/Protocol132.cpp b/source/Protocol/Protocol132.cpp new file mode 100644 index 000000000..33f404113 --- /dev/null +++ b/source/Protocol/Protocol132.cpp @@ -0,0 +1,769 @@ + +// Protocol132.cpp + +// Implements the cProtocol132 class representing the release 1.3.2 protocol (#39) + +#include "Globals.h" +#include "Protocol132.h" +#include "../cRoot.h" +#include "../cServer.h" +#include "../cClientHandle.h" +#include "../../CryptoPP/randpool.h" +#include "../cItem.h" +#include "../ChunkDataSerializer.h" +#include "../cPlayer.h" +#include "../cMonster.h" +#include "../UI/Window.h" + + + + + +#define HANDLE_PACKET_READ(Proc, Type, Var) \ + Type Var; \ + { \ + if (!m_ReceivedData.Proc(Var)) \ + { \ + return PARSE_INCOMPLETE; \ + } \ + } + + + + +typedef unsigned char Byte; + + + + + +using namespace CryptoPP; + + + + + +const int MAX_ENC_LEN = 512; // Maximum size of the encrypted message; should be 128, but who knows... + + + + + +enum +{ + PACKET_KEEP_ALIVE = 0x00, + PACKET_LOGIN = 0x01, + PACKET_ENTITY_EQUIPMENT = 0x05, + PACKET_COMPASS = 0x06, + PACKET_PLAYER_SPAWN = 0x14, + PACKET_SPAWN_MOB = 0x18, + PACKET_DESTROY_ENTITIES = 0x1d, + PACKET_CHUNK_DATA = 0x33, + PACKET_BLOCK_CHANGE = 0x35, + PACKET_BLOCK_ACTION = 0x36, + PACKET_SOUND_EFFECT = 0x3e +} ; + + + + + +// Converts a raw 160-bit SHA1 digest into a Java Hex representation +// According to http://wiki.vg/wiki/index.php?title=Protocol_Encryption&oldid=2802 +static void DigestToJava(byte a_Digest[20], AString & a_Out) +{ + bool IsNegative = (a_Digest[0] >= 0x80); + if (IsNegative) + { + // Two's complement: + bool carry = true; // Add one to the whole number + for (int i = 19; i >= 0; i--) + { + a_Digest[i] = ~a_Digest[i]; + if (carry) + { + carry = (a_Digest[i] == 0xff); + a_Digest[i]++; + } + } + } + a_Out.clear(); + a_Out.reserve(40); + for (int i = 0; i < 20; i++) + { + AppendPrintf(a_Out, "%02x", a_Digest[i]); + } + while ((a_Out.length() > 0) && (a_Out[0] == '0')) + { + a_Out.erase(0, 1); + } + if (IsNegative) + { + a_Out.insert(0, "-"); + } +} + + + + + +/* +// Self-test the hash formatting for known values: +// sha1(Notch) : 4ed1f46bbe04bc756bcb17c0c7ce3e4632f06a48 +// sha1(jeb_) : -7c9d5b0044c130109a5d7b5fb5c317c02b4e28c1 +// sha1(simon) : 88e16a1019277b15d58faf0541e11910eb756f6 + +class Test +{ +public: + Test(void) + { + AString DigestNotch, DigestJeb, DigestSimon; + byte Digest[20]; + CryptoPP::SHA1 Checksum; + Checksum.Update((const byte *)"Notch", 5); + Checksum.Final(Digest); + DigestToJava(Digest, DigestNotch); + Checksum.Restart(); + Checksum.Update((const byte *)"jeb_", 4); + Checksum.Final(Digest); + DigestToJava(Digest, DigestJeb); + Checksum.Restart(); + Checksum.Update((const byte *)"simon", 5); + Checksum.Final(Digest); + DigestToJava(Digest, DigestSimon); + printf("Notch: \"%s\"", DigestNotch.c_str()); + printf("jeb_: \"%s\"", DigestJeb.c_str()); + printf("simon: \"%s\"", DigestSimon.c_str()); + } +} test; +*/ + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cProtocol132: + +cProtocol132::cProtocol132(cClientHandle * a_Client) : + super(a_Client), + m_IsEncrypted(false) +{ +} + + + + + +cProtocol132::~cProtocol132() +{ + if (!m_DataToSend.empty()) + { + LOGD("There are %d unsent bytes while deleting cProtocol132", m_DataToSend.size()); + } +} + + + + + +void cProtocol132::DataReceived(const char * a_Data, int a_Size) +{ + if (m_IsEncrypted) + { + byte Decrypted[512]; + while (a_Size > 0) + { + int NumBytes = (a_Size > sizeof(Decrypted)) ? sizeof(Decrypted) : a_Size; + m_Decryptor.ProcessData(Decrypted, (byte *)a_Data, NumBytes); + super::DataReceived((const char *)Decrypted, NumBytes); + a_Size -= NumBytes; + a_Data += NumBytes; + } + } + else + { + super::DataReceived(a_Data, a_Size); + } +} + + + + + +void cProtocol132::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_BLOCK_ACTION); + WriteInt (a_BlockX); + WriteShort((short)a_BlockY); + WriteInt (a_BlockZ); + WriteByte (a_Byte1); + WriteByte (a_Byte2); + WriteShort(a_BlockType); + Flush(); +} + + + + + +void cProtocol132::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_BLOCK_CHANGE); + WriteInt (a_BlockX); + WriteByte ((unsigned char)a_BlockY); + WriteInt (a_BlockZ); + WriteShort(a_BlockType); + WriteByte (a_BlockMeta); + Flush(); +} + + + + + +void cProtocol132::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) +{ + cCSLock Lock(m_CSPacket); + + // Pre-chunk not used in 1.3.2. Finally. + + // Send the chunk data: + AString Serialized = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_3_2); + WriteByte(PACKET_CHUNK_DATA); + WriteInt (a_ChunkX); + WriteInt (a_ChunkZ); + SendData(Serialized.data(), Serialized.size()); + Flush(); +} + + + + + +void cProtocol132::SendDestroyEntity(const cEntity & a_Entity) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_DESTROY_ENTITIES); + WriteByte(1); // entity count + WriteInt (a_Entity.GetUniqueID()); + Flush(); +} + + + + + +void cProtocol132::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_ENTITY_EQUIPMENT); + WriteInt (a_Entity.GetUniqueID()); + WriteShort(a_SlotNum); + WriteItem (a_Item); + Flush(); +} + + + + + +void cProtocol132::SendKeepAlive(int a_PingID) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_KEEP_ALIVE); + WriteInt (a_PingID); + Flush(); +} + + + + + +void cProtocol132::SendLogin(const cPlayer & a_Player, const cWorld & a_World) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_LOGIN); + WriteInt (a_Player.GetUniqueID()); // EntityID of the player + WriteString("default"); // Level type + WriteByte ((int)a_Player.GetGameMode()); + WriteByte (0); // TODO: Dimension (Nether / Overworld / End) + WriteByte (2); // TODO: Difficulty + WriteByte (0); // Unused, used to be world height + WriteByte (8); // Client list width or something + Flush(); + + SendCompass(a_World); + + // Send the initial position (so that confirmation works, FS #245): + SendPlayerMoveLook(); +} + + + + + +void cProtocol132::SendPlayerSpawn(const cPlayer & a_Player) +{ + const cItem & HeldItem = a_Player.GetEquippedItem(); + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_PLAYER_SPAWN); + WriteInt (a_Player.GetUniqueID()); + WriteString(a_Player.GetName()); + WriteInt ((int)(a_Player.GetPosX() * 32)); + WriteInt ((int)(a_Player.GetPosY() * 32)); + WriteInt ((int)(a_Player.GetPosZ() * 32)); + WriteByte ((char)((a_Player.GetRot().x / 360.f) * 256)); + WriteByte ((char)((a_Player.GetRot().y / 360.f) * 256)); + WriteShort (HeldItem.IsEmpty() ? 0 : HeldItem.m_ItemType); + // Player metadata: just use a default metadata value, since the client doesn't like starting without any metadata: + WriteByte (0); // Index 0, byte (flags) + WriteByte (0); // Flags, empty + WriteByte (0x7f); // End of metadata + Flush(); +} + + + + + +void cProtocol132::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_SOUND_EFFECT); + WriteString (a_SoundName); + WriteInt (a_SrcX); + WriteInt (a_SrcY); + WriteInt (a_SrcZ); + WriteFloat (a_Volume); + WriteByte ((char)(a_Pitch * 63.0f)); + Flush(); +} + + + + + +void cProtocol132::SendSpawnMob(const cMonster & a_Mob) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_SPAWN_MOB); + WriteInt (a_Mob.GetUniqueID()); + WriteByte (a_Mob.GetMobType()); + WriteVectorI((Vector3i)(a_Mob.GetPosition() * 32)); + WriteByte (0); // yaw + WriteByte (0); // pitch + WriteByte (0); // head yaw + WriteShort (0); // Velocity Z + WriteShort (0); // Velocity X + WriteShort (0); // Velocity Y + AString MetaData = GetEntityMetaData(a_Mob); + SendData (MetaData.data(), MetaData.size()); + Flush(); +} + + + + + +void cProtocol132::SendUnloadChunk(int a_ChunkX, int a_ChunkZ) +{ + // Not used in 1.3.2 + // Does it unload chunks on its own? +} + + + + + +void cProtocol132::SendWholeInventory(const cWindow & a_Window) +{ + // 1.3.2 requires player inventory slots to be sent as SetSlot packets, + // otherwise it sometimes fails to update the window + super::SendWholeInventory(a_Window); + const cItem * Slots = m_Client->GetPlayer()->GetInventory().GetSlots(); + int BaseOffset = a_Window.GetNumSlots() - cInventory::c_NumSlots + cInventory::c_MainOffset; // the number of non-inventory slots the window has; inventory follows + char WindowID = a_Window.GetWindowID(); + for (int i = 0; i < cInventory::c_NumSlots - cInventory::c_MainOffset; i++) + { + SendInventorySlot(WindowID, BaseOffset + i, Slots[i + cInventory::c_MainOffset]); + } // for i - Slots[] + // Send even the item being dragged: + SendInventorySlot(-1, -1, m_Client->GetPlayer()->GetDraggingItem()); +} + + + + + +AString cProtocol132::GetAuthServerID(void) +{ + // http://wiki.vg/wiki/index.php?title=Session&oldid=2615 + // Server uses SHA1 to mix ServerID, Client secret and server public key together + // The mixing is done in StartEncryption, the result is in m_AuthServerID + + return m_AuthServerID; +} + + + + + +int cProtocol132::ParsePacket(unsigned char a_PacketType) +{ + switch (a_PacketType) + { + default: return super::ParsePacket(a_PacketType); // off-load previously known packets into cProtocol125 + case 0xcc: return ParseLocaleViewDistance(); + case 0xcd: return ParseClientStatuses(); + case 0xfc: return ParseEncryptionKeyResponse(); + } +} + + + + + +int cProtocol132::ParseBlockPlace(void) +{ + HANDLE_PACKET_READ(ReadBEInt, int, PosX); + HANDLE_PACKET_READ(ReadByte, Byte, PosY); + HANDLE_PACKET_READ(ReadBEInt, int, PosZ); + HANDLE_PACKET_READ(ReadChar, char, Direction); + + cItem HeldItem; + int res = ParseItem(HeldItem); + if (res < 0) + { + return res; + } + + HANDLE_PACKET_READ(ReadChar, char, CursorX); + HANDLE_PACKET_READ(ReadChar, char, CursorY); + HANDLE_PACKET_READ(ReadChar, char, CursorZ); + + m_Client->HandleBlockPlace(PosX, PosY, PosZ, Direction, HeldItem); + return PARSE_OK; +} + + + + + +int cProtocol132::ParseHandshake(void) +{ + HANDLE_PACKET_READ(ReadByte, Byte, ProtocolVersion); + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Username); + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, ServerHost); + HANDLE_PACKET_READ(ReadBEInt, int, ServerPort); + m_Username = Username; + + if (!m_Client->HandleHandshake( m_Username )) + { + return PARSE_OK; // Player is not allowed into the server + } + + // Send a 0xFD Encryption Key Request http://wiki.vg/Protocol#0xFD + CryptoPP::StringSink sink(m_ServerPublicKey); // GCC won't allow inline instantiation in the following line, damned temporary refs + cRoot::Get()->GetServer()->GetPublicKey().Save(sink); + SendEncryptionKeyRequest(); + + return PARSE_OK; +} + + + + + +int cProtocol132::ParseClientStatuses(void) +{ + HANDLE_PACKET_READ(ReadByte, byte, Status); + m_Client->HandleLogin(39, m_Username); + return PARSE_OK; +} + + + + + +int cProtocol132::ParseEncryptionKeyResponse(void) +{ + HANDLE_PACKET_READ(ReadBEShort, short, EncKeyLength); + AString EncKey; + if (!m_ReceivedData.ReadString(EncKey, EncKeyLength)) + { + return PARSE_INCOMPLETE; + } + HANDLE_PACKET_READ(ReadBEShort, short, EncNonceLength); + AString EncNonce; + if (!m_ReceivedData.ReadString(EncNonce, EncNonceLength)) + { + return PARSE_INCOMPLETE; + } + if ((EncKeyLength > MAX_ENC_LEN) || (EncNonceLength > MAX_ENC_LEN)) + { + LOGD("Too long encryption"); + m_Client->Kick("Hacked client"); + return PARSE_OK; + } + + HandleEncryptionKeyResponse(EncKey, EncNonce); + return PARSE_OK; +} + + + + + +int cProtocol132::ParseLocaleViewDistance(void) +{ + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Locale); + HANDLE_PACKET_READ(ReadChar, char, ViewDistance); + HANDLE_PACKET_READ(ReadChar, char, ChatFlags); + HANDLE_PACKET_READ(ReadChar, char, ClientDifficulty); + // TODO: m_Client->HandleLocale(Locale); + // TODO: m_Client->HandleViewDistance(ViewDistance); + // TODO: m_Client->HandleChatFlags(ChatFlags); + // Ignoring client difficulty + return PARSE_OK; +} + + + + + +int cProtocol132::ParseLogin(void) +{ + // Login packet not used in 1.3.2 + return PARSE_ERROR; +} + + + + + +int cProtocol132::ParsePlayerAbilities(void) +{ + HANDLE_PACKET_READ(ReadBool, bool, Flags); + HANDLE_PACKET_READ(ReadChar, char, FlyingSpeed); + HANDLE_PACKET_READ(ReadChar, char, WalkingSpeed); + // TODO: m_Client->HandlePlayerAbilities(...); + return PARSE_OK; +} + + + + + +void cProtocol132::SendData(const char * a_Data, int a_Size) +{ + m_DataToSend.append(a_Data, a_Size); +} + + + + + +void cProtocol132::Flush(void) +{ + ASSERT(m_CSPacket.IsLockedByCurrentThread()); // Did all packets lock the CS properly? + + if (m_DataToSend.empty()) + { + LOGD("Flushing empty"); + return; + } + const char * a_Data = m_DataToSend.data(); + int a_Size = m_DataToSend.size(); + if (m_IsEncrypted) + { + byte Encrypted[8192]; // Larger buffer, we may be sending lots of data (chunks) + while (a_Size > 0) + { + int NumBytes = (a_Size > sizeof(Encrypted)) ? sizeof(Encrypted) : a_Size; + m_Encryptor.ProcessData(Encrypted, (byte *)a_Data, NumBytes); + super::SendData((const char *)Encrypted, NumBytes); + a_Size -= NumBytes; + a_Data += NumBytes; + } + } + else + { + super::SendData(a_Data, a_Size); + } + m_DataToSend.clear(); +} + + + + + +void cProtocol132::WriteItem(const cItem & a_Item) +{ + short ItemType = a_Item.m_ItemType; + ASSERT(ItemType >= -1); // Check validity of packets in debug runtime + if (ItemType <= 0) + { + // Fix, to make sure no invalid values are sent. + ItemType = -1; + } + + if (a_Item.IsEmpty()) + { + WriteShort(-1); + return; + } + + WriteShort(ItemType); + WriteByte (a_Item.m_ItemCount); + WriteShort(a_Item.m_ItemDamage); + + // TODO: Implement enchantments + WriteShort(-1); +} + + + + + +int cProtocol132::ParseItem(cItem & a_Item) +{ + HANDLE_PACKET_READ(ReadBEShort, short, ItemType); + + if (ItemType <= -1) + { + a_Item.Empty(); + return PARSE_OK; + } + a_Item.m_ItemType = ItemType; + + HANDLE_PACKET_READ(ReadChar, char, ItemCount); + HANDLE_PACKET_READ(ReadBEShort, short, ItemDamage); + a_Item.m_ItemCount = ItemCount; + // a_Item.m_ItemDamage = ItemDamage; + if (ItemCount <= 0) + { + a_Item.Empty(); + } + + HANDLE_PACKET_READ(ReadBEShort, short, EnchantNumBytes); + if (EnchantNumBytes <= 0) + { + return PARSE_OK; + } + + // TODO: Enchantment not implemented yet! + if (!m_ReceivedData.SkipRead(EnchantNumBytes)) + { + return PARSE_INCOMPLETE; + } + + return PARSE_OK; +} + + + + + +void cProtocol132::SendCompass(const cWorld & a_World) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_COMPASS); + WriteInt((int)(a_World.GetSpawnX())); + WriteInt((int)(a_World.GetSpawnY())); + WriteInt((int)(a_World.GetSpawnZ())); + Flush(); +} + + + + + +void cProtocol132::SendEncryptionKeyRequest(void) +{ + cCSLock Lock(m_CSPacket); + WriteByte((char)0xfd); + WriteString(cRoot::Get()->GetServer()->GetServerID()); + WriteShort((short)m_ServerPublicKey.size()); + SendData(m_ServerPublicKey.data(), m_ServerPublicKey.size()); + WriteShort(4); + WriteInt((int)(intptr_t)this); // Using 'this' as the cryptographic nonce, so that we don't have to generate one each time :) + Flush(); +} + + + + + +void cProtocol132::HandleEncryptionKeyResponse(const AString & a_EncKey, const AString & a_EncNonce) +{ + // Decrypt EncNonce using privkey + RSAES::Decryptor rsaDecryptor(cRoot::Get()->GetServer()->GetPrivateKey()); + time_t CurTime = time(NULL); + CryptoPP::RandomPool rng; + rng.Put((const byte *)&CurTime, sizeof(CurTime)); + byte DecryptedNonce[MAX_ENC_LEN]; + DecodingResult res = rsaDecryptor.Decrypt(rng, (const byte *)a_EncNonce.data(), a_EncNonce.size(), DecryptedNonce); + if (!res.isValidCoding || (res.messageLength != 4)) + { + LOGD("Bad nonce length"); + m_Client->Kick("Hacked client"); + return; + } + if (ntohl(*((int *)DecryptedNonce)) != (unsigned)(uintptr_t)this) + { + LOGD("Bad nonce value"); + m_Client->Kick("Hacked client"); + return; + } + + // Decrypt the symmetric encryption key using privkey: + byte DecryptedKey[MAX_ENC_LEN]; + res = rsaDecryptor.Decrypt(rng, (const byte *)a_EncKey.data(), a_EncKey.size(), DecryptedKey); + if (!res.isValidCoding || (res.messageLength != 16)) + { + LOGD("Bad key length"); + m_Client->Kick("Hacked client"); + return; + } + + { + // Send encryption key response: + cCSLock Lock(m_CSPacket); + WriteByte((char)0xfc); + WriteShort(0); + WriteShort(0); + Flush(); + } + + StartEncryption(DecryptedKey); + return; +} + + + + + +void cProtocol132::StartEncryption(const byte * a_Key) +{ + m_Encryptor.SetKey(a_Key, 16, MakeParameters(Name::IV(), ConstByteArrayParameter(a_Key, 16))(Name::FeedbackSize(), 1)); + m_Decryptor.SetKey(a_Key, 16, MakeParameters(Name::IV(), ConstByteArrayParameter(a_Key, 16))(Name::FeedbackSize(), 1)); + m_IsEncrypted = true; + + // Prepare the m_AuthServerID: + CryptoPP::SHA1 Checksum; + AString ServerID = cRoot::Get()->GetServer()->GetServerID(); + Checksum.Update((const byte *)ServerID.c_str(), ServerID.length()); + Checksum.Update(a_Key, 16); + Checksum.Update((const byte *)m_ServerPublicKey.c_str(), m_ServerPublicKey.length()); + byte Digest[20]; + Checksum.Final(Digest); + DigestToJava(Digest, m_AuthServerID); +} + + + + diff --git a/source/Protocol/Protocol132.h b/source/Protocol/Protocol132.h new file mode 100644 index 000000000..dde34ca30 --- /dev/null +++ b/source/Protocol/Protocol132.h @@ -0,0 +1,99 @@ + +// Protocol132.h + +// Interfaces to the cProtocol132 class representing the release 1.3.2 protocol (#39) + + + + + +#pragma once + +#include "Protocol125.h" +#include "../../CryptoPP/modes.h" +#include "../../CryptoPP/aes.h" + + + + + +class cProtocol132 : + public cProtocol125 +{ + typedef cProtocol125 super; +public: + + cProtocol132(cClientHandle * a_Client); + + // DEBUG: + virtual ~cProtocol132(); + + /// Called when client sends some data: + virtual void DataReceived(const char * a_Data, int a_Size) override; + + // Sending commands: + virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override; + virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; + virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override; + virtual void SendDestroyEntity (const cEntity & a_Entity) override; + virtual void SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override; + virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override; + virtual void SendPlayerSpawn (const cPlayer & a_Player) override; + virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; // a_Src coords are Block * 8 + virtual void SendSpawnMob (const cMonster & a_Mob) override; + virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override; + virtual void SendWholeInventory (const cWindow & a_Window) override; + + virtual AString GetAuthServerID(void) override; + + // DEBUG: + virtual void SendKeepAlive (int a_PingID) override; + + /// Handling of the additional packets: + virtual int ParsePacket(unsigned char a_PacketType) override; + + // Modified packets: + virtual int ParseBlockPlace (void) override; + virtual int ParseHandshake (void) override; + virtual int ParseLogin (void) override; + virtual int ParsePlayerAbilities(void) override; + + // New packets: + virtual int ParseClientStatuses (void); + virtual int ParseEncryptionKeyResponse(void); + virtual int ParseLocaleViewDistance (void); + +protected: + bool m_IsEncrypted; + CryptoPP::CFB_Mode::Decryption m_Decryptor; + CryptoPP::CFB_Mode::Encryption m_Encryptor; + AString m_DataToSend; + + /// The ServerID used for session authentication; set in StartEncryption(), used in GetAuthServerID() + AString m_AuthServerID; + + /// The server's public key, as used by SendEncryptionKeyRequest() and StartEncryption() + AString m_ServerPublicKey; + + virtual void SendData(const char * a_Data, int a_Size) override; + + // DEBUG: + virtual void Flush(void) override; + + // Items in slots are sent differently + virtual void WriteItem(const cItem & a_Item) override; + virtual int ParseItem(cItem & a_Item) override; + + virtual void SendCompass(const cWorld & a_World); + virtual void SendEncryptionKeyRequest(void); + + /// Decrypts the key and nonce, checks nonce, starts the symmetric encryption + void HandleEncryptionKeyResponse(const AString & a_EncKey, const AString & a_EncNonce); + + /// Starts the symmetric encryption with the specified key; also sets m_AuthServerID + void StartEncryption(const byte * a_Key); +} ; + + + + diff --git a/source/Protocol/ProtocolRecognizer.cpp b/source/Protocol/ProtocolRecognizer.cpp new file mode 100644 index 000000000..18bf3b0b3 --- /dev/null +++ b/source/Protocol/ProtocolRecognizer.cpp @@ -0,0 +1,542 @@ + +// ProtocolRecognizer.cpp + +// Implements the cProtocolRecognizer class representing the meta-protocol that recognizes possibly multiple +// protocol versions and redirects everything to them + +#include "Globals.h" + +#include "ProtocolRecognizer.h" +#include "Protocol125.h" +#include "Protocol132.h" +#include "../cClientHandle.h" +#include "../cRoot.h" +#include "../cWorld.h" +#include "../cChatColor.h" + + + + + +cProtocolRecognizer::cProtocolRecognizer(cClientHandle * a_Client) : + super(a_Client), + m_Protocol(NULL), + m_Buffer(512) +{ +} + + + + + +void cProtocolRecognizer::DataReceived(const char * a_Data, int a_Size) +{ + if (m_Protocol == NULL) + { + if (!m_Buffer.Write(a_Data, a_Size)) + { + m_Client->Kick("Unsupported protocol version"); + return; + } + + if (!TryRecognizeProtocol()) + { + return; + } + + // 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()); + } + else + { + m_Protocol->DataReceived(a_Data, a_Size); + } +} + + + + + +void cProtocolRecognizer::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendBlockAction(a_BlockX, a_BlockY, a_BlockZ, a_Byte1, a_Byte2, a_BlockType); +} + + + + + +void cProtocolRecognizer::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + ASSERT(m_Protocol != NULL); + 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 != NULL); + m_Protocol->SendBlockChanges(a_ChunkX, a_ChunkZ, a_Changes); +} + + + + + +void cProtocolRecognizer::SendChat(const AString & a_Message) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendChat(a_Message); +} + + + + + +void cProtocolRecognizer::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendChunkData(a_ChunkX, a_ChunkZ, a_Serializer); +} + + + + + +void cProtocolRecognizer::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendCollectPickup(a_Pickup, a_Player); +} + + + + + +void cProtocolRecognizer::SendDestroyEntity(const cEntity & a_Entity) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendDestroyEntity(a_Entity); +} + + + + + +void cProtocolRecognizer::SendDisconnect(const AString & a_Reason) +{ + if (m_Protocol != NULL) + { + m_Protocol->SendDisconnect(a_Reason); + } + else + { + // This is used when the client sends a server-ping, respond with the default packet: + WriteByte ((char)0xff); // PACKET_DISCONNECT + WriteString(a_Reason); + } +} + + + + + +void cProtocolRecognizer::SendEntHeadLook(const cEntity & a_Entity) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendEntHeadLook(a_Entity); +} + + + + + +void cProtocolRecognizer::SendEntLook(const cEntity & a_Entity) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendEntLook(a_Entity); +} + + + + + +void cProtocolRecognizer::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendEntityEquipment(a_Entity, a_SlotNum, a_Item); +} + + + + + +void cProtocolRecognizer::SendEntityStatus(const cEntity & a_Entity, char a_Status) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendEntityStatus(a_Entity, a_Status); +} + + + + + +void cProtocolRecognizer::SendGameMode(eGameMode a_GameMode) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendGameMode(a_GameMode); +} + + + + + +void cProtocolRecognizer::SendHealth(void) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendHealth(); +} + + + + + +void cProtocolRecognizer::SendInventoryProgress(char a_WindowID, short a_Progressbar, short a_Value) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendInventoryProgress(a_WindowID, a_Progressbar, a_Value); +} + + + + + +void cProtocolRecognizer::SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem & a_Item) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendInventorySlot(a_WindowID, a_SlotNum, a_Item); +} + + + + + +void cProtocolRecognizer::SendKeepAlive(int a_PingID) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendKeepAlive(a_PingID); +} + + + + + +void cProtocolRecognizer::SendLogin(const cPlayer & a_Player, const cWorld & a_World) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendLogin(a_Player, a_World); +} + + + + + +void cProtocolRecognizer::SendMetadata(const cEntity & a_Entity) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendMetadata(a_Entity); +} + + + + + +void cProtocolRecognizer::SendPickupSpawn(const cPickup & a_Pickup) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendPickupSpawn(a_Pickup); +} + + + + + +void cProtocolRecognizer::SendPlayerAnimation(const cPlayer & a_Player, char a_Animation) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendPlayerAnimation(a_Player, a_Animation); +} + + + + + +void cProtocolRecognizer::SendPlayerListItem(const cPlayer & a_Player, bool a_IsOnline) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendPlayerListItem(a_Player, a_IsOnline); +} + + + + + +void cProtocolRecognizer::SendPlayerMoveLook(void) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendPlayerMoveLook(); +} + + + + + +void cProtocolRecognizer::SendPlayerPosition(void) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendPlayerPosition(); +} + + + + + +void cProtocolRecognizer::SendPlayerSpawn(const cPlayer & a_Player) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendPlayerSpawn(a_Player); +} + + + + + +void cProtocolRecognizer::SendEntRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendEntRelMove(a_Entity, a_RelX, a_RelY, a_RelZ); +} + + + + + +void cProtocolRecognizer::SendEntRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendEntRelMoveLook(a_Entity, a_RelX, a_RelY, a_RelZ); +} + + + + + +void cProtocolRecognizer::SendRespawn(void) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendRespawn(); +} + + + + +void cProtocolRecognizer::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) { + ASSERT(m_Protocol != NULL); + m_Protocol->SendSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch); +} + + + + + +void cProtocolRecognizer::SendSpawnMob(const cMonster & a_Mob) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendSpawnMob(a_Mob); +} + + + + + +void cProtocolRecognizer::SendTeleportEntity(const cEntity & a_Entity) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendTeleportEntity(a_Entity); +} + + + + + +void cProtocolRecognizer::SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendThunderbolt(a_BlockX, a_BlockY, a_BlockZ); +} + + + + + +void cProtocolRecognizer::SendTimeUpdate(Int64 a_WorldTime) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendTimeUpdate(a_WorldTime); +} + + + + + +void cProtocolRecognizer::SendUnloadChunk(int a_ChunkX, int a_ChunkZ) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendUnloadChunk(a_ChunkX, a_ChunkZ); +} + + + + + +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 != NULL); + m_Protocol->SendUpdateSign(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4); +} + + + + + +void cProtocolRecognizer::SendWeather(eWeather a_Weather) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendWeather(a_Weather); +} + + + + + +void cProtocolRecognizer::SendWholeInventory(const cInventory & a_Inventory) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendWholeInventory(a_Inventory); +} + + + + + +void cProtocolRecognizer::SendWholeInventory(const cWindow & a_Window) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendWholeInventory(a_Window); +} + + + + + +void cProtocolRecognizer::SendWindowClose(char a_WindowID) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendWindowClose(a_WindowID); +} + + + + + +void cProtocolRecognizer::SendWindowOpen(char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendWindowOpen(a_WindowID, a_WindowType, a_WindowTitle, a_NumSlots); +} + + + + + +AString cProtocolRecognizer::GetAuthServerID(void) +{ + ASSERT(m_Protocol != NULL); + return m_Protocol->GetAuthServerID(); +} + + + + + +void cProtocolRecognizer::SendData(const char * a_Data, int a_Size) +{ + // This is used only when handling the server ping + m_Client->SendData(a_Data, a_Size); +} + + + + + +bool cProtocolRecognizer::TryRecognizeProtocol(void) +{ + // 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 + + // The first packet should be a Handshake, 0x02: + unsigned char PacketType; + if (!m_Buffer.ReadByte(PacketType)) + { + return false; + } + switch (PacketType) + { + case 0x02: break; // Handshake, continue recognizing + case 0xfe: HandleServerPing(); return false; + default: return false; + } + + // 1.3.2 starts with 0x02 0x39 + // 1.2.5 starts with 0x02 and name is expected to less than 0x3900 long :) + char ch; + if (!m_Buffer.ReadChar(ch)) + { + return false; + } + if (ch == 39) + { + m_Protocol = new cProtocol132(m_Client); + return true; + } + m_Protocol = new cProtocol125(m_Client); + return true; +} + + + + + +void cProtocolRecognizer::HandleServerPing(void) +{ + AString Reply; + Printf(Reply, "%s%s%i%s%i", + cRoot::Get()->GetDefaultWorld()->GetDescription().c_str(), + cChatColor::Delimiter.c_str(), + cRoot::Get()->GetDefaultWorld()->GetNumPlayers(), + cChatColor::Delimiter.c_str(), + cRoot::Get()->GetDefaultWorld()->GetMaxPlayers() + ); + m_Client->Kick(Reply.c_str()); +} + + + + diff --git a/source/Protocol/ProtocolRecognizer.h b/source/Protocol/ProtocolRecognizer.h new file mode 100644 index 000000000..3eff8d2ab --- /dev/null +++ b/source/Protocol/ProtocolRecognizer.h @@ -0,0 +1,99 @@ + +// ProtocolRecognizer.h + +// Interfaces to the cProtocolRecognizer class representing the meta-protocol that recognizes possibly multiple +// protocol versions and redirects everything to them + + + + + +#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.2.4, 1.2.5, 1.3.1, 1.3.2" +#define MCS_PROTOCOL_VERSIONS "29, 39" + + + + + +class cProtocolRecognizer : + public cProtocol +{ + typedef cProtocol super; + +public: + cProtocolRecognizer(cClientHandle * a_Client); + + /// Called when client sends some data: + virtual void DataReceived(const char * a_Data, int a_Size) override; + + /// Sending stuff to clients: + virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) 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 SendChat (const AString & a_Message) override; + virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override; + virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override; + virtual void SendDestroyEntity (const cEntity & a_Entity) override; + virtual void SendDisconnect (const AString & a_Reason) override; + virtual void SendEntHeadLook (const cEntity & a_Entity) override; + virtual void SendEntLook (const cEntity & a_Entity) override; + virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override; + virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) override; + virtual void SendEntRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override; + virtual void SendEntRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override; + virtual void SendGameMode (eGameMode a_GameMode) override; + virtual void SendHealth (void) override; + virtual void SendInventoryProgress(char a_WindowID, short a_Progressbar, short a_Value) override; + virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override; + virtual void SendKeepAlive (int a_PingID) override; + virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override; + virtual void SendMetadata (const cEntity & a_Entity) override; + virtual void SendPickupSpawn (const cPickup & a_Pickup) override; + virtual void SendPlayerAnimation (const cPlayer & a_Player, char a_Animation) override; + virtual void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline) override; + virtual void SendPlayerMoveLook (void) override; + virtual void SendPlayerPosition (void) override; + virtual void SendPlayerSpawn (const cPlayer & a_Player) override; + virtual void SendRespawn (void) override; + virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; + virtual void SendSpawnMob (const cMonster & a_Mob) override; + virtual void SendTeleportEntity (const cEntity & a_Entity) override; + virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override; + virtual void SendTimeUpdate (Int64 a_WorldTime) override; + virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) 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 SendWeather (eWeather a_Weather) override; + virtual void SendWholeInventory (const cInventory & a_Inventory) override; + virtual void SendWholeInventory (const cWindow & a_Window) override; + virtual void SendWindowClose (char a_WindowID) override; + virtual void SendWindowOpen (char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots) override; + + virtual AString GetAuthServerID(void) override; + + virtual void SendData(const char * a_Data, int a_Size) override; + +protected: + cProtocol * m_Protocol; //< The recognized protocol + cByteBuffer m_Buffer; //< Buffer for the incoming data until we recognize the protocol + + /// Tries to recognize protocol based on m_Buffer contents; returns true if recognized + bool TryRecognizeProtocol(void); + + /// Called when the recognizer gets a server ping packet; responds with server stats and destroys the client + void HandleServerPing(void); +} ; + + + + + -- cgit v1.2.3