From 7824ce9d6352664bd291e57a3084e5c51af5d4a6 Mon Sep 17 00:00:00 2001 From: "madmaxoft@gmail.com" Date: Fri, 31 Aug 2012 21:59:57 +0000 Subject: Progress on the 1.3.2 protocol. Sometimes the client lets the player through, but most of the times the connection breaks for no apparent reason. git-svn-id: http://mc-server.googlecode.com/svn/trunk@812 0a769ca7-a7f5-676a-18bf-c427514a06d6 --- source/Protocol132.cpp | 339 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 319 insertions(+), 20 deletions(-) (limited to 'source/Protocol132.cpp') diff --git a/source/Protocol132.cpp b/source/Protocol132.cpp index 2a4a830c6..5949776d3 100644 --- a/source/Protocol132.cpp +++ b/source/Protocol132.cpp @@ -9,6 +9,10 @@ #include "cServer.h" #include "cClientHandle.h" #include "CryptoPP/osrng.h" +#include "cItem.h" +#include "ChunkDataSerializer.h" +#include "cPlayer.h" +#include "cMonster.h" @@ -44,11 +48,33 @@ const int MAX_ENC_LEN = 512; // Maximum size of the encrypted message; should b +enum +{ + PACKET_KEEP_ALIVE = 0x00, + PACKET_LOGIN = 0x01, + PACKET_PLAYER_SPAWN = 0x14, + PACKET_SPAWN_MOB = 0x18, + PACKET_DESTROY_ENTITIES = 0x1d, + PACKET_CHUNK_DATA = 0x33, + PACKET_BLOCK_CHANGE = 0x34, + PACKET_BLOCK_ACTION = 0x36, +} ; + + + + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cProtocol132: cProtocol132::cProtocol132(cClientHandle * a_Client) : super(a_Client), + // DEBUG: + m_CurrentIn(0), + m_CurrentOut(0), + m_EncIn(0), + m_EncOut(0), + m_IsEncrypted(false) { } @@ -57,10 +83,24 @@ cProtocol132::cProtocol132(cClientHandle * a_Client) : +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) { + m_CurrentIn += a_Size; if (m_IsEncrypted) { + m_EncIn += a_Size; byte Decrypted[512]; while (a_Size > 0) { @@ -81,11 +121,169 @@ void cProtocol132::DataReceived(const char * a_Data, int 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: + // Chunk data seems to break the connection for some reason; without it, the connection lives indefinitely + 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::SendKeepAlive(int a_PingID) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_KEEP_ALIVE); + WriteInt (a_PingID); + Flush(); +} + + + + + +void cProtocol132::SendLogin(const cPlayer & a_Player) +{ + 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(); +} + + + + + +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::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? +} + + + + + int cProtocol132::ParsePacket(unsigned char a_PacketType) { + // DEBUG: + LOGD("Received packet 0x%02x, current bytecount %d, enc %d", a_PacketType, m_CurrentIn, m_EncIn); + 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(); } @@ -95,6 +293,32 @@ int cProtocol132::ParsePacket(unsigned char a_PacketType) +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); @@ -107,13 +331,14 @@ int cProtocol132::ParseHandshake(void) CryptoPP::StringSink sink(key); // GCC won't allow inline instantiation in the following line, damned temporary refs cRoot::Get()->GetServer()->GetPublicKey().Save(sink); - // Send a 0xFD Encryption Key Request http://wiki.vg/Protocol#Encryption_Key_Request_.280xFD.29 + // Send a 0xFD Encryption Key Request http://wiki.vg/Protocol#0xFD WriteByte((char)0xfd); WriteString("MCServer"); WriteShort((short)key.size()); SendData(key.data(), key.size()); WriteShort(4); WriteInt((int)this); // Using 'this' as the cryptographic nonce, so that we don't have to generate one each time :) + Flush(); return PARSE_OK; } @@ -121,27 +346,10 @@ int cProtocol132::ParseHandshake(void) -int cProtocol132::ParseLogin(void) -{ - // Login packet not used in 1.3.2 - return PARSE_ERROR; -} - - - - - int cProtocol132::ParseClientStatuses(void) { HANDLE_PACKET_READ(ReadByte, byte, Status); - - // DEBUG: - // Kick the client, we don't have all the packets yet and sending wrong packets makes the client freeze - // m_Client->Kick("I don't speak your language (yet)"); - - // TODO: - // m_Client->HandleLogin(39, m_Username); - + m_Client->HandleLogin(39, m_Username); return PARSE_OK; } @@ -178,11 +386,72 @@ int cProtocol132::ParseEncryptionKeyResponse(void) +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) { + // DEBUG: + m_DataToSend.append(a_Data, a_Size); + m_CurrentOut += a_Size; +} + + + + + +void cProtocol132::Flush(void) +{ + // DEBUG + if (m_DataToSend.empty()) + { + LOGD("Flushing empty"); + return; + } + LOGD("Flushing packet 0x%02x, %d bytes; %d bytes out, %d enc out", (unsigned char)m_DataToSend[0], m_DataToSend.size(), m_CurrentOut, m_EncOut); + const char * a_Data = m_DataToSend.data(); + int a_Size = m_DataToSend.size(); if (m_IsEncrypted) { - byte Encrypted[1024]; // Larger buffer, we may be sending lots of data (chunks) + m_EncOut += a_Size; + byte Encrypted[4096]; // Larger buffer, we may be sending lots of data (chunks) while (a_Size > 0) { int NumBytes = (a_Size > sizeof(Encrypted)) ? sizeof(Encrypted) : a_Size; @@ -196,6 +465,35 @@ void cProtocol132::SendData(const char * a_Data, int a_Size) { super::SendData(a_Data, a_Size); } + m_DataToSend.clear(); + // cSleep::MilliSleep(300); +} + + + + + +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; + } + + WriteShort(ItemType); + if (a_Item.IsEmpty()) + { + return; + } + + WriteByte (a_Item.m_ItemCount); + WriteShort(a_Item.m_ItemDamage); + + // TODO: Implement enchantments + WriteShort(-1); } @@ -236,6 +534,7 @@ void cProtocol132::HandleEncryptionKeyResponse(const AString & a_EncKey, const A WriteByte((char)0xfc); WriteShort(0); WriteShort(0); + Flush(); StartEncryption(DecryptedKey); return; -- cgit v1.2.3