diff options
Diffstat (limited to '')
-rw-r--r-- | src/Protocol/ChunkDataSerializer.cpp | 176 | ||||
-rw-r--r-- | src/Protocol/ChunkDataSerializer.h (renamed from source/Protocol/ChunkDataSerializer.h) | 0 | ||||
-rw-r--r-- | src/Protocol/Protocol.h (renamed from source/Protocol/Protocol.h) | 0 | ||||
-rw-r--r-- | src/Protocol/Protocol125.cpp (renamed from source/Protocol/Protocol125.cpp) | 0 | ||||
-rw-r--r-- | src/Protocol/Protocol125.h (renamed from source/Protocol/Protocol125.h) | 0 | ||||
-rw-r--r-- | src/Protocol/Protocol132.cpp | 950 | ||||
-rw-r--r-- | src/Protocol/Protocol132.h | 102 | ||||
-rw-r--r-- | src/Protocol/Protocol14x.cpp | 256 | ||||
-rw-r--r-- | src/Protocol/Protocol14x.h (renamed from source/Protocol/Protocol14x.h) | 0 | ||||
-rw-r--r-- | src/Protocol/Protocol15x.cpp (renamed from source/Protocol/Protocol15x.cpp) | 0 | ||||
-rw-r--r-- | src/Protocol/Protocol15x.h (renamed from source/Protocol/Protocol15x.h) | 0 | ||||
-rw-r--r-- | src/Protocol/Protocol16x.cpp (renamed from source/Protocol/Protocol16x.cpp) | 0 | ||||
-rw-r--r-- | src/Protocol/Protocol16x.h (renamed from source/Protocol/Protocol16x.h) | 0 | ||||
-rw-r--r-- | src/Protocol/Protocol17x.cpp (renamed from source/Protocol/Protocol17x.cpp) | 0 | ||||
-rw-r--r-- | src/Protocol/Protocol17x.h | 259 | ||||
-rw-r--r-- | src/Protocol/ProtocolRecognizer.cpp (renamed from source/Protocol/ProtocolRecognizer.cpp) | 0 | ||||
-rw-r--r-- | src/Protocol/ProtocolRecognizer.h (renamed from source/Protocol/ProtocolRecognizer.h) | 0 |
17 files changed, 1743 insertions, 0 deletions
diff --git a/src/Protocol/ChunkDataSerializer.cpp b/src/Protocol/ChunkDataSerializer.cpp new file mode 100644 index 000000000..d406b8b51 --- /dev/null +++ b/src/Protocol/ChunkDataSerializer.cpp @@ -0,0 +1,176 @@ + +// ChunkDataSerializer.cpp + +// Implements the cChunkDataSerializer class representing the object that can: +// - serialize chunk data to different protocol versions +// - cache such serialized data for multiple clients + +#include "Globals.h" +#include "ChunkDataSerializer.h" +#include "lib/zlib/zlib.h" + + + + +cChunkDataSerializer::cChunkDataSerializer( + const cChunkDef::BlockTypes & a_BlockTypes, + const cChunkDef::BlockNibbles & a_BlockMetas, + const cChunkDef::BlockNibbles & a_BlockLight, + const cChunkDef::BlockNibbles & a_BlockSkyLight, + const unsigned char * a_BiomeData +) : + m_BlockTypes(a_BlockTypes), + m_BlockMetas(a_BlockMetas), + m_BlockLight(a_BlockLight), + m_BlockSkyLight(a_BlockSkyLight), + m_BiomeData(a_BiomeData) +{ +} + + + + +const AString & cChunkDataSerializer::Serialize(int a_Version) +{ + Serializations::const_iterator itr = m_Serializations.find(a_Version); + if (itr != m_Serializations.end()) + { + return itr->second; + } + + AString data; + switch (a_Version) + { + case RELEASE_1_2_5: Serialize29(data); break; + case RELEASE_1_3_2: Serialize39(data); break; + // TODO: Other protocol versions may serialize the data differently; implement here + + default: + { + LOGERROR("cChunkDataSerializer::Serialize(): Unknown version: %d", a_Version); + ASSERT(!"Unknown chunk data serialization version"); + break; + } + } + m_Serializations[a_Version] = data; + return m_Serializations[a_Version]; +} + + + + + +void cChunkDataSerializer::Serialize29(AString & a_Data) +{ + // TODO: Do not copy data and then compress it; rather, compress partial blocks of data (zlib *can* stream) + + const int BiomeDataSize = cChunkDef::Width * cChunkDef::Width; + const int MetadataOffset = sizeof(m_BlockTypes); + const int BlockLightOffset = MetadataOffset + sizeof(m_BlockMetas); + const int SkyLightOffset = BlockLightOffset + sizeof(m_BlockLight); + const int BiomeOffset = SkyLightOffset + sizeof(m_BlockSkyLight); + const int DataSize = BiomeOffset + BiomeDataSize; + + // Temporary buffer for the composed data: + char AllData [DataSize]; + + memcpy(AllData, m_BlockTypes, sizeof(m_BlockTypes)); + memcpy(AllData + MetadataOffset, m_BlockMetas, sizeof(m_BlockMetas)); + memcpy(AllData + BlockLightOffset, m_BlockLight, sizeof(m_BlockLight)); + memcpy(AllData + SkyLightOffset, m_BlockSkyLight, sizeof(m_BlockSkyLight)); + memcpy(AllData + BiomeOffset, m_BiomeData, BiomeDataSize); + + // Compress the data: + // In order not to use allocation, use a fixed-size buffer, with the size + // that uses the same calculation as compressBound(): + const uLongf CompressedMaxSize = DataSize + (DataSize >> 12) + (DataSize >> 14) + (DataSize >> 25) + 16; + char CompressedBlockData[CompressedMaxSize]; + + uLongf CompressedSize = compressBound(DataSize); + + // Run-time check that our compile-time guess about CompressedMaxSize was enough: + ASSERT(CompressedSize <= CompressedMaxSize); + + compress2((Bytef*)CompressedBlockData, &CompressedSize, (const Bytef*)AllData, sizeof(AllData), Z_DEFAULT_COMPRESSION); + + // Now put all those data into a_Data: + + // "Ground-up continuous", or rather, "biome data present" flag: + a_Data.push_back('\x01'); + + // Two bitmaps; we're aways sending the full chunk with no additional data, so the bitmaps are 0xffff and 0, respectively + // Also, no endian flipping is needed because of the const values + unsigned short BitMap1 = 0xffff; + unsigned short BitMap2 = 0; + a_Data.append((const char *)&BitMap1, sizeof(short)); + a_Data.append((const char *)&BitMap2, sizeof(short)); + + Int32 CompressedSizeBE = htonl(CompressedSize); + a_Data.append((const char *)&CompressedSizeBE, sizeof(CompressedSizeBE)); + + Int32 UnusedInt32 = 0; + a_Data.append((const char *)&UnusedInt32, sizeof(UnusedInt32)); + + a_Data.append(CompressedBlockData, CompressedSize); +} + + + + + +void cChunkDataSerializer::Serialize39(AString & a_Data) +{ + // TODO: Do not copy data and then compress it; rather, compress partial blocks of data (zlib *can* stream) + + const int BiomeDataSize = cChunkDef::Width * cChunkDef::Width; + const int MetadataOffset = sizeof(m_BlockTypes); + const int BlockLightOffset = MetadataOffset + sizeof(m_BlockMetas); + const int SkyLightOffset = BlockLightOffset + sizeof(m_BlockLight); + const int BiomeOffset = SkyLightOffset + sizeof(m_BlockSkyLight); + const int DataSize = BiomeOffset + BiomeDataSize; + + // Temporary buffer for the composed data: + char AllData [DataSize]; + + memcpy(AllData, m_BlockTypes, sizeof(m_BlockTypes)); + memcpy(AllData + MetadataOffset, m_BlockMetas, sizeof(m_BlockMetas)); + memcpy(AllData + BlockLightOffset, m_BlockLight, sizeof(m_BlockLight)); + memcpy(AllData + SkyLightOffset, m_BlockSkyLight, sizeof(m_BlockSkyLight)); + memcpy(AllData + BiomeOffset, m_BiomeData, BiomeDataSize); + + // Compress the data: + // In order not to use allocation, use a fixed-size buffer, with the size + // that uses the same calculation as compressBound(): + const uLongf CompressedMaxSize = DataSize + (DataSize >> 12) + (DataSize >> 14) + (DataSize >> 25) + 16; + char CompressedBlockData[CompressedMaxSize]; + + uLongf CompressedSize = compressBound(DataSize); + + // Run-time check that our compile-time guess about CompressedMaxSize was enough: + ASSERT(CompressedSize <= CompressedMaxSize); + + compress2((Bytef*)CompressedBlockData, &CompressedSize, (const Bytef*)AllData, sizeof(AllData), Z_DEFAULT_COMPRESSION); + + // Now put all those data into a_Data: + + // "Ground-up continuous", or rather, "biome data present" flag: + a_Data.push_back('\x01'); + + // Two bitmaps; we're aways sending the full chunk with no additional data, so the bitmaps are 0xffff and 0, respectively + // Also, no endian flipping is needed because of the const values + unsigned short BitMap1 = 0xffff; + unsigned short BitMap2 = 0; + a_Data.append((const char *)&BitMap1, sizeof(short)); + a_Data.append((const char *)&BitMap2, sizeof(short)); + + Int32 CompressedSizeBE = htonl(CompressedSize); + a_Data.append((const char *)&CompressedSizeBE, sizeof(CompressedSizeBE)); + + // Unlike 29, 39 doesn't have the "unused" int + + a_Data.append(CompressedBlockData, CompressedSize); +} + + + + diff --git a/source/Protocol/ChunkDataSerializer.h b/src/Protocol/ChunkDataSerializer.h index a42856356..a42856356 100644 --- a/source/Protocol/ChunkDataSerializer.h +++ b/src/Protocol/ChunkDataSerializer.h diff --git a/source/Protocol/Protocol.h b/src/Protocol/Protocol.h index 542060ece..542060ece 100644 --- a/source/Protocol/Protocol.h +++ b/src/Protocol/Protocol.h diff --git a/source/Protocol/Protocol125.cpp b/src/Protocol/Protocol125.cpp index 54be65b12..54be65b12 100644 --- a/source/Protocol/Protocol125.cpp +++ b/src/Protocol/Protocol125.cpp diff --git a/source/Protocol/Protocol125.h b/src/Protocol/Protocol125.h index c5f44c818..c5f44c818 100644 --- a/source/Protocol/Protocol125.h +++ b/src/Protocol/Protocol125.h diff --git a/src/Protocol/Protocol132.cpp b/src/Protocol/Protocol132.cpp new file mode 100644 index 000000000..8f7891e4b --- /dev/null +++ b/src/Protocol/Protocol132.cpp @@ -0,0 +1,950 @@ + +// Protocol132.cpp + +// Implements the cProtocol132 class representing the release 1.3.2 protocol (#39) + +#include "Globals.h" +#include "ChunkDataSerializer.h" +#include "lib/cryptopp/randpool.h" +#include "Protocol132.h" +#include "../Root.h" +#include "../Server.h" +#include "../World.h" +#include "../ClientHandle.h" +#include "../Item.h" +#include "../Entities/Player.h" +#include "../Mobs/Monster.h" +#include "../UI/Window.h" +#include "../Entities/Pickup.h" +#include "../WorldStorage/FastNBT.h" +#include "../StringCompression.h" + + + + + +#define HANDLE_PACKET_READ(Proc, Type, Var) \ + Type Var; \ + { \ + if (!m_ReceivedData.Proc(Var)) \ + { \ + m_ReceivedData.CheckValid(); \ + return PARSE_INCOMPLETE; \ + } \ + m_ReceivedData.CheckValid(); \ + } + + + + +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_COLLECT_PICKUP = 0x16, + PACKET_SPAWN_MOB = 0x18, + PACKET_DESTROY_ENTITIES = 0x1d, + PACKET_CHUNK_DATA = 0x33, + PACKET_BLOCK_CHANGE = 0x35, + PACKET_BLOCK_ACTION = 0x36, + PACKET_BLOCK_BREAK_ANIM = 0x37, + PACKET_SOUND_EFFECT = 0x3e, + PACKET_SOUND_PARTICLE_EFFECT = 0x3d, + PACKET_TAB_COMPLETION = 0xcb, + PACKET_LOCALE_VIEW_DISTANCE = 0xcc, + PACKET_CLIENT_STATUSES = 0xcd, + PACKET_ENCRYPTION_KEY_RESP = 0xfc, +} ; + + + + + +// 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::SendBlockBreakAnim(int a_entityID, int a_BlockX, int a_BlockY, int a_BlockZ, char stage) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_BLOCK_BREAK_ANIM); + WriteInt (a_entityID); + WriteInt (a_BlockX); + WriteInt (a_BlockY); + WriteInt (a_BlockZ); + WriteByte (stage); + 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::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(); + + // Also send the "pop" sound effect with a somewhat random pitch (fast-random using EntityID ;) + SendSoundEffect( + "random.pop", + (int)(a_Pickup.GetPosX() * 8), (int)(a_Pickup.GetPosY() * 8), (int)(a_Pickup.GetPosZ() * 8), + 0.5, (float)(0.75 + ((float)((a_Pickup.GetUniqueID() * 23) % 32)) / 64) + ); +} + + + + + +void cProtocol132::SendDestroyEntity(const cEntity & a_Entity) +{ + if (a_Entity.GetUniqueID() == m_Client->GetPlayer()->GetUniqueID()) + { + // Do not send "destroy self" to the client, the client would crash (FS #254) + return; + } + + 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::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 ((Byte)(a_World.GetDimension())); + WriteByte (2); // TODO: Difficulty + WriteByte (0); // Unused, used to be world height + WriteByte (8); // Client list width or something + Flush(); + + SendCompass(a_World); +} + + + + + +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::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_SOUND_PARTICLE_EFFECT); + WriteInt (a_EffectID); + WriteInt (a_SrcX); + WriteByte(a_SrcY); + WriteInt (a_SrcZ); + WriteInt (a_Data); + 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 ((Byte)((a_Mob.GetRotation() / 360.f) * 256)); + WriteByte ((Byte)((a_Mob.GetPitch() / 360.f) * 256)); + WriteByte ((Byte)((a_Mob.GetHeadYaw() / 360.f) * 256)); + WriteShort ((short)(a_Mob.GetSpeedX() * 400)); + WriteShort ((short)(a_Mob.GetSpeedY() * 400)); + WriteShort ((short)(a_Mob.GetSpeedZ() * 400)); + + WriteCommonMetadata(a_Mob); + WriteMobMetadata(a_Mob); + WriteByte(0x7f); + + Flush(); +} + + + + + +void cProtocol132::SendTabCompletionResults(const AStringVector & a_Results) +{ + if (a_Results.empty()) + { + // No results to send + return; + } + + AString Serialized(a_Results[0]); + for (AStringVector::const_iterator itr = a_Results.begin() + 1, end = a_Results.end(); itr != end; ++itr) + { + Serialized.push_back(0); + Serialized.append(*itr); + } // for itr - a_Results[] + + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_TAB_COMPLETION); + WriteString(Serialized); + Flush(); +} + + + + + +void cProtocol132::SendUnloadChunk(int a_ChunkX, int a_ChunkZ) +{ + // Unloading the chunk is done by sending a "map chunk" packet + // with IncludeInitialize set to true and primary bitmap set to 0: + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_CHUNK_DATA); + WriteInt (a_ChunkX); + WriteInt (a_ChunkZ); + WriteBool(true); // IncludeInitialize + WriteShort(0); // Primary bitmap + WriteShort(0); // Add bitmap + WriteInt(0); + Flush(); +} + + + + + +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 + + // Send the entire window: + super::SendWholeInventory(a_Window); + + // Send the player inventory and hotbar: + const cInventory & Inventory = m_Client->GetPlayer()->GetInventory(); + int BaseOffset = a_Window.GetNumSlots() - (cInventory::invNumSlots - cInventory::invInventoryOffset); // Number of non-inventory slots + char WindowID = a_Window.GetWindowID(); + for (int i = 0; i < cInventory::invInventoryCount; i++) + { + SendInventorySlot(WindowID, BaseOffset + i, Inventory.GetInventorySlot(i)); + } // for i - Inventory[] + BaseOffset += cInventory::invInventoryCount; + for (int i = 0; i < cInventory::invHotbarCount; i++) + { + SendInventorySlot(WindowID, BaseOffset + i, Inventory.GetHotbarSlot(i)); + } // for i - Hotbar[] + + // 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 PACKET_CLIENT_STATUSES: return ParseClientStatuses(); + case PACKET_ENCRYPTION_KEY_RESP: return ParseEncryptionKeyResponse(); + case PACKET_LOCALE_VIEW_DISTANCE: return ParseLocaleViewDistance(); + case PACKET_TAB_COMPLETION: return ParseTabCompletion(); + } +} + + + + + +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, BlockFace); + + 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->HandleRightClick(PosX, PosY, PosZ, BlockFace, CursorX, CursorY, CursorZ, 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); + if ((Status & 1) == 0) + { + m_Client->HandleLogin(39, m_Username); + } + else + { + m_Client->HandleRespawn(); + } + 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; +} + + + + + +int cProtocol132::ParseTabCompletion(void) +{ + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Text); + m_Client->HandleTabCompletion(Text); + 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); + + if (a_Item.m_Enchantments.IsEmpty()) + { + WriteShort(-1); + return; + } + + // Send the enchantments: + cFastNBTWriter Writer; + const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench"; + a_Item.m_Enchantments.WriteToNBTCompound(Writer, TagName); + Writer.Finish(); + AString Compressed; + CompressStringGZIP(Writer.GetResult().data(), Writer.GetResult().size(), Compressed); + WriteShort(Compressed.size()); + SendData(Compressed.data(), Compressed.size()); +} + + + + + +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, MetadataLength); + if (MetadataLength <= 0) + { + return PARSE_OK; + } + + // Read the metadata + AString Metadata; + Metadata.resize(MetadataLength); + if (!m_ReceivedData.ReadBuf((void *)Metadata.data(), MetadataLength)) + { + return PARSE_INCOMPLETE; + } + + return ParseItemMetadata(a_Item, Metadata); +} + + + + + +int cProtocol132::ParseItemMetadata(cItem & a_Item, const AString & a_Metadata) +{ + // Uncompress the GZIPped data: + AString Uncompressed; + if (UncompressStringGZIP(a_Metadata.data(), a_Metadata.size(), Uncompressed) != Z_OK) + { + AString HexDump; + CreateHexDump(HexDump, a_Metadata.data(), a_Metadata.size(), 16); + LOG("Cannot unGZIP item metadata:\n%s", HexDump.c_str()); + return PARSE_ERROR; + } + + // Parse into NBT: + cParsedNBT NBT(Uncompressed.data(), Uncompressed.size()); + if (!NBT.IsValid()) + { + AString HexDump; + CreateHexDump(HexDump, Uncompressed.data(), Uncompressed.size(), 16); + LOG("Cannot parse NBT item metadata:\n%s", HexDump.c_str()); + return PARSE_ERROR; + } + + // Load enchantments from the NBT: + for (int tag = NBT.GetFirstChild(NBT.GetRoot()); tag >= 0; tag = NBT.GetNextSibling(tag)) + { + if ( + (NBT.GetType(tag) == TAG_List) && + ( + (NBT.GetName(tag) == "ench") || + (NBT.GetName(tag) == "StoredEnchantments") + ) + ) + { + a_Item.m_Enchantments.ParseFromNBT(NBT, tag); + } + } + + 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<PKCS1v15>::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/src/Protocol/Protocol132.h b/src/Protocol/Protocol132.h new file mode 100644 index 000000000..2023d7960 --- /dev/null +++ b/src/Protocol/Protocol132.h @@ -0,0 +1,102 @@ + +// Protocol132.h + +// Interfaces to the cProtocol132 class representing the release 1.3.2 protocol (#39) + + + + + +#pragma once + +#include "Protocol125.h" +#include "lib/cryptopp/modes.h" +#include "lib/cryptopp/aes.h" + + + + + +class cProtocol132 : + public cProtocol125 +{ + typedef cProtocol125 super; +public: + + cProtocol132(cClientHandle * a_Client); + virtual ~cProtocol132(); + + /// Called when client sends some data: + virtual void DataReceived(const char * a_Data, int a_Size) override; + + // Sending commands (alphabetically sorted): + 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 (int 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 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 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 SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override; + virtual void SendSpawnMob (const cMonster & a_Mob) override; + virtual void SendTabCompletionResults(const AStringVector & a_Results) override; + virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override; + virtual void SendWholeInventory (const cWindow & a_Window) override; + + virtual AString GetAuthServerID(void) 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); + virtual int ParseTabCompletion (void); + +protected: + bool m_IsEncrypted; + CryptoPP::CFB_Mode<CryptoPP::AES>::Decryption m_Decryptor; + CryptoPP::CFB_Mode<CryptoPP::AES>::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; + + /// Parses the metadata that may come with the item. + int ParseItemMetadata(cItem & a_Item, const AString & a_Metadata); + + 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/src/Protocol/Protocol14x.cpp b/src/Protocol/Protocol14x.cpp new file mode 100644 index 000000000..ea406d7a2 --- /dev/null +++ b/src/Protocol/Protocol14x.cpp @@ -0,0 +1,256 @@ + +// Protocol14x.cpp + +/* +Implements the 1.4.x protocol classes representing these protocols: +- cProtocol142: + - release 1.4.2 protocol (#47) + - release 1.4.4 protocol (#49) - the same protocol class is used, because the only difference is in a packet that MCServer doesn't implement yet (ITEM_DATA) + - release 1.4.5 protocol (same as 1.4.4) +- cProtocol146: + - release 1.4.6 protocol (#51) +*/ + +#include "Globals.h" +#include "Protocol14x.h" +#include "../Root.h" +#include "../Server.h" +#include "../ClientHandle.h" +#include "lib/cryptopp/randpool.h" +#include "../Item.h" +#include "ChunkDataSerializer.h" +#include "../Entities/Player.h" +#include "../Mobs/Monster.h" +#include "../UI/Window.h" +#include "../Entities/Pickup.h" +#include "../Entities/FallingBlock.h" + + + + + +#define HANDLE_PACKET_READ(Proc, Type, Var) \ + Type Var; \ + { \ + if (!m_ReceivedData.Proc(Var)) \ + { \ + m_ReceivedData.CheckValid(); \ + return PARSE_INCOMPLETE; \ + } \ + m_ReceivedData.CheckValid(); \ + } + + + + + +enum +{ + PACKET_UPDATE_TIME = 0x04, + PACKET_PICKUP_SPAWN = 0x15, + PACKET_SPAWN_OBJECT = 0x17, + PACKET_ENTITY_METADATA = 0x28, + PACKET_SOUND_PARTICLE_EFFECT = 0x3d +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cProtocol142: + +cProtocol142::cProtocol142(cClientHandle * a_Client) : + super(a_Client) +{ +} + + + + + +int cProtocol142::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); + HANDLE_PACKET_READ(ReadChar, char, ShouldShowCape); // <-- new in 1.4.2 + // TODO: m_Client->HandleLocale(Locale); + // TODO: m_Client->HandleViewDistance(ViewDistance); + // TODO: m_Client->HandleChatFlags(ChatFlags); + // Ignoring client difficulty + return PARSE_OK; +} + + + + + +void cProtocol142::SendPickupSpawn(const cPickup & a_Pickup) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_PICKUP_SPAWN); + WriteInt (a_Pickup.GetUniqueID()); + WriteItem (a_Pickup.GetItem()); + 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 cProtocol142::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_SOUND_PARTICLE_EFFECT); + WriteInt (a_EffectID); + WriteInt (a_SrcX); + WriteByte(a_SrcY); + WriteInt (a_SrcZ); + WriteInt (a_Data); + WriteBool(0); + Flush(); +} + + + + + +void cProtocol142::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_UPDATE_TIME); + WriteInt64(a_WorldAge); + WriteInt64(a_TimeOfDay); + Flush(); +} + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cProtocol146: + +cProtocol146::cProtocol146(cClientHandle * a_Client) : + super(a_Client) +{ +} + + + + + +void cProtocol146::SendPickupSpawn(const cPickup & a_Pickup) +{ + ASSERT(!a_Pickup.GetItem().IsEmpty()); + + cCSLock Lock(m_CSPacket); + + // Send a SPAWN_OBJECT packet for the base entity: + WriteByte(PACKET_SPAWN_OBJECT); + WriteInt (a_Pickup.GetUniqueID()); + WriteByte(0x02); + WriteInt ((int)(a_Pickup.GetPosX() * 32)); + WriteInt ((int)(a_Pickup.GetPosY() * 32)); + WriteInt ((int)(a_Pickup.GetPosZ() * 32)); + WriteInt (1); + WriteShort((short)(a_Pickup.GetSpeed().x * 32)); + WriteShort((short)(a_Pickup.GetSpeed().y * 32)); + WriteShort((short)(a_Pickup.GetSpeed().z * 32)); + WriteByte(0); + WriteByte(0); + + // Send a ENTITY_METADATA packet with the slot info: + WriteByte(PACKET_ENTITY_METADATA); + WriteInt(a_Pickup.GetUniqueID()); + WriteByte(0xaa); // a slot value at index 10 + WriteItem(a_Pickup.GetItem()); + WriteByte(0x7f); // End of metadata + Flush(); +} + + + + + +void cProtocol146::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock) +{ + // Send a spawn object / vehicle packet + cCSLock Lock(m_CSPacket); + + WriteByte(PACKET_SPAWN_OBJECT); + WriteInt (a_FallingBlock.GetUniqueID()); + WriteByte(70); + WriteInt ((int)(a_FallingBlock.GetPosX() * 32)); + WriteInt ((int)(a_FallingBlock.GetPosY() * 32)); + WriteInt ((int)(a_FallingBlock.GetPosZ() * 32)); + WriteByte (0); // Pitch + WriteByte (0); // Yaw + WriteInt (a_FallingBlock.GetBlockType()); // data indicator = blocktype + WriteShort((short)(a_FallingBlock.GetSpeedX() * 400)); + WriteShort((short)(a_FallingBlock.GetSpeedY() * 400)); + WriteShort((short)(a_FallingBlock.GetSpeedZ() * 400)); + Flush(); +} + + + + + +void cProtocol146::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_SPAWN_OBJECT); + WriteInt (a_Entity.GetUniqueID()); + WriteByte(a_ObjectType); + WriteInt ((int)(a_Entity.GetPosX() * 32)); + WriteInt ((int)(a_Entity.GetPosY() * 32)); + WriteInt ((int)(a_Entity.GetPosZ() * 32)); + WriteByte(a_Pitch); + WriteByte(a_Yaw); + WriteInt (a_ObjectData); + if (a_ObjectData != 0) + { + WriteShort((short)(a_Entity.GetSpeedX() * 400)); + WriteShort((short)(a_Entity.GetSpeedY() * 400)); + WriteShort((short)(a_Entity.GetSpeedZ() * 400)); + } + Flush(); +} + + + + + +void cProtocol146::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_SPAWN_OBJECT); + WriteInt (a_Vehicle.GetUniqueID()); + WriteByte (a_VehicleType); + WriteInt ((int)(a_Vehicle.GetPosX() * 32)); + WriteInt ((int)(a_Vehicle.GetPosY() * 32)); + WriteInt ((int)(a_Vehicle.GetPosZ() * 32)); + WriteByte ((Byte)((a_Vehicle.GetPitch() / 360.f) * 256)); + WriteByte ((Byte)((a_Vehicle.GetRotation() / 360.f) * 256)); + WriteInt (a_VehicleSubType); + if (a_VehicleSubType != 0) + { + WriteShort((short)(a_Vehicle.GetSpeedX() * 400)); + WriteShort((short)(a_Vehicle.GetSpeedY() * 400)); + WriteShort((short)(a_Vehicle.GetSpeedZ() * 400)); + } + Flush(); +} + + + + + diff --git a/source/Protocol/Protocol14x.h b/src/Protocol/Protocol14x.h index ca497bbc1..ca497bbc1 100644 --- a/source/Protocol/Protocol14x.h +++ b/src/Protocol/Protocol14x.h diff --git a/source/Protocol/Protocol15x.cpp b/src/Protocol/Protocol15x.cpp index c337d26e7..c337d26e7 100644 --- a/source/Protocol/Protocol15x.cpp +++ b/src/Protocol/Protocol15x.cpp diff --git a/source/Protocol/Protocol15x.h b/src/Protocol/Protocol15x.h index e554fe130..e554fe130 100644 --- a/source/Protocol/Protocol15x.h +++ b/src/Protocol/Protocol15x.h diff --git a/source/Protocol/Protocol16x.cpp b/src/Protocol/Protocol16x.cpp index cfa27b3c4..cfa27b3c4 100644 --- a/source/Protocol/Protocol16x.cpp +++ b/src/Protocol/Protocol16x.cpp diff --git a/source/Protocol/Protocol16x.h b/src/Protocol/Protocol16x.h index 325e41c5a..325e41c5a 100644 --- a/source/Protocol/Protocol16x.h +++ b/src/Protocol/Protocol16x.h diff --git a/source/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp index ae1df7395..ae1df7395 100644 --- a/source/Protocol/Protocol17x.cpp +++ b/src/Protocol/Protocol17x.cpp diff --git a/src/Protocol/Protocol17x.h b/src/Protocol/Protocol17x.h new file mode 100644 index 000000000..449727854 --- /dev/null +++ b/src/Protocol/Protocol17x.h @@ -0,0 +1,259 @@ + +// Protocol17x.h + +/* +Declares the 1.7.x protocol classes: + - cProtocol172 + - release 1.7.2 protocol (#4) +(others may be added later in the future for the 1.7 release series) +*/ + + + + + +#pragma once + +#include "Protocol.h" +#include "../ByteBuffer.h" +#include "lib/cryptopp/modes.h" +#include "lib/cryptopp/aes.h" + + + + + +class cProtocol172 : + public cProtocol // TODO +{ + typedef cProtocol super; // TODO + +public: + + cProtocol172(cClientHandle * a_Client, const AString & a_ServerAddress, UInt16 a_ServerPort, UInt32 a_State); + + /// Called when client sends some data: + virtual void DataReceived(const char * a_Data, int 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 (int 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 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 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 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 SendEntityProperties (const cEntity & a_Entity) override; + virtual void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override; + virtual void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) 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 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 SendPickupSpawn (const cPickup & a_Pickup) override; + virtual void SendPlayerAbilities (void) 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 SendPlayerMaxSpeed (void) 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 SendExperience (void) override; + virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override; + virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override; + virtual void SendSpawnMob (const cMonster & a_Mob) override; + virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) override; + virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) override; + virtual void SendTabCompletionResults(const AStringVector & a_Results) 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_WorldAge, Int64 a_TimeOfDay) 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 SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) 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 { return m_AuthServerID; } + +protected: + + /// Composes individual packets in the protocol's m_OutPacketBuffer; sends them upon being destructed + class cPacketizer + { + public: + cPacketizer(cProtocol172 & a_Protocol, UInt32 a_PacketType) : + m_Protocol(a_Protocol), + m_Out(a_Protocol.m_OutPacketBuffer), + m_Lock(a_Protocol.m_CSPacket) + { + m_Out.WriteVarInt(a_PacketType); + } + + ~cPacketizer(); + + void WriteBool(bool a_Value) + { + m_Out.WriteBool(a_Value); + } + + void WriteByte(Byte a_Value) + { + m_Out.WriteByte(a_Value); + } + + void WriteChar(char a_Value) + { + m_Out.WriteChar(a_Value); + } + + void WriteShort(short a_Value) + { + m_Out.WriteBEShort(a_Value); + } + + void WriteInt(int a_Value) + { + m_Out.WriteBEInt(a_Value); + } + + void WriteInt64(Int64 a_Value) + { + m_Out.WriteBEInt64(a_Value); + } + + void WriteFloat(float a_Value) + { + m_Out.WriteBEFloat(a_Value); + } + + void WriteDouble(double a_Value) + { + m_Out.WriteBEDouble(a_Value); + } + + void WriteVarInt(UInt32 a_Value) + { + m_Out.WriteVarInt(a_Value); + } + + void WriteString(const AString & a_Value) + { + m_Out.WriteVarUTF8String(a_Value); + } + + void WriteBuf(const char * a_Data, int a_Size) + { + m_Out.Write(a_Data, a_Size); + } + + void WriteItem(const cItem & a_Item); + void WriteByteAngle(double a_Angle); // Writes the specified angle using a single byte + void WriteFPInt(double a_Value); // Writes the double value as a 27:5 fixed-point integer + void WriteEntityMetadata(const cEntity & a_Entity); // Writes the metadata for the specified entity, not including the terminating 0x7f + void WriteMobMetadata(const cMonster & a_Mob); // Writes the mob-specific metadata for the specified mob + void WriteEntityProperties(const cEntity & a_Entity); // Writes the entity properties for the specified entity, including the Count field + + protected: + cProtocol172 & m_Protocol; + cByteBuffer & m_Out; + cCSLock m_Lock; + } ; + + AString m_ServerAddress; + + UInt16 m_ServerPort; + + AString m_AuthServerID; + + /// State of the protocol. 1 = status, 2 = login, 3 = game + UInt32 m_State; + + /// Buffer for the received data + cByteBuffer m_ReceivedData; + + /// Buffer for composing the outgoing packets, through cPacketizer + cByteBuffer m_OutPacketBuffer; + + /// Buffer for composing packet length (so that each cPacketizer instance doesn't allocate a new cPacketBuffer) + cByteBuffer m_OutPacketLenBuffer; + + bool m_IsEncrypted; + CryptoPP::CFB_Mode<CryptoPP::AES>::Decryption m_Decryptor; + CryptoPP::CFB_Mode<CryptoPP::AES>::Encryption m_Encryptor; + + + /// Adds the received (unencrypted) data to m_ReceivedData, parses complete packets + void AddReceivedData(const char * a_Data, int a_Size); + + /// Reads and handles the packet. The packet length and type have already been read. + void HandlePacket(UInt32 a_PacketType, UInt32 a_RemainingBytes); + + // Packet handlers while in the Status state (m_State == 1): + void HandlePacketStatusPing (UInt32 a_RemainingBytes); + void HandlePacketStatusRequest(UInt32 a_RemainingBytes); + + // Packet handlers while in the Login state (m_State == 2): + void HandlePacketLoginEncryptionResponse(UInt32 a_RemainingBytes); + void HandlePacketLoginStart (UInt32 a_RemainingBytes); + + // Packet handlers while in the Game state (m_State == 3): + void HandlePacketAnimation (UInt32 a_RemainingBytes); + void HandlePacketBlockDig (UInt32 a_RemainingBytes); + void HandlePacketBlockPlace (UInt32 a_RemainingBytes); + void HandlePacketChatMessage (UInt32 a_RemainingBytes); + void HandlePacketClientSettings (UInt32 a_RemainingBytes); + void HandlePacketClientStatus (UInt32 a_RemainingBytes); + void HandlePacketCreativeInventoryAction(UInt32 a_RemainingBytes); + void HandlePacketEntityAction (UInt32 a_RemainingBytes); + void HandlePacketKeepAlive (UInt32 a_RemainingBytes); + void HandlePacketPlayer (UInt32 a_RemainingBytes); + void HandlePacketPlayerAbilities (UInt32 a_RemainingBytes); + void HandlePacketPlayerLook (UInt32 a_RemainingBytes); + void HandlePacketPlayerPos (UInt32 a_RemainingBytes); + void HandlePacketPlayerPosLook (UInt32 a_RemainingBytes); + void HandlePacketPluginMessage (UInt32 a_RemainingBytes); + void HandlePacketSlotSelect (UInt32 a_RemainingBytes); + void HandlePacketSteerVehicle (UInt32 a_RemainingBytes); + void HandlePacketTabComplete (UInt32 a_RemainingBytes); + void HandlePacketUpdateSign (UInt32 a_RemainingBytes); + void HandlePacketUseEntity (UInt32 a_RemainingBytes); + void HandlePacketWindowClick (UInt32 a_RemainingBytes); + void HandlePacketWindowClose (UInt32 a_RemainingBytes); + + + /// Writes an entire packet into the output stream. a_Packet is expected to start with the packet type; data length is prepended here. + void WritePacket(cByteBuffer & a_Packet); + + /// Sends the data to the client, encrypting them if needed. + virtual void SendData(const char * a_Data, int a_Size) override; + + void SendCompass(const cWorld & a_World); + + /// Reads an item out of the received data, sets a_Item to the values read. Returns false if not enough received data + bool ReadItem(cItem & a_Item); + + /// Parses item metadata as read by ReadItem(), into the item enchantments. + void ParseItemMetadata(cItem & a_Item, const AString & a_Metadata); +} ; + + + + diff --git a/source/Protocol/ProtocolRecognizer.cpp b/src/Protocol/ProtocolRecognizer.cpp index 64bd83075..64bd83075 100644 --- a/source/Protocol/ProtocolRecognizer.cpp +++ b/src/Protocol/ProtocolRecognizer.cpp diff --git a/source/Protocol/ProtocolRecognizer.h b/src/Protocol/ProtocolRecognizer.h index 03f48fb35..03f48fb35 100644 --- a/source/Protocol/ProtocolRecognizer.h +++ b/src/Protocol/ProtocolRecognizer.h |