summaryrefslogtreecommitdiffstats
path: root/src/Protocol
diff options
context:
space:
mode:
authorMattes D <github@xoft.cz>2013-11-27 09:23:17 +0100
committerMattes D <github@xoft.cz>2013-11-27 09:23:17 +0100
commit49760db89d94ede5d123d927141a6cd60dbaaf07 (patch)
tree6c6cf99e4cf3128311a93cd187947b502f3732a0 /src/Protocol
parentcWorld::SpawnExperienceOrb() now returns the entity ID of the spawned orb. (diff)
parentFixed VC2008 compilation, normalized include paths. (diff)
downloadcuberite-49760db89d94ede5d123d927141a6cd60dbaaf07.tar
cuberite-49760db89d94ede5d123d927141a6cd60dbaaf07.tar.gz
cuberite-49760db89d94ede5d123d927141a6cd60dbaaf07.tar.bz2
cuberite-49760db89d94ede5d123d927141a6cd60dbaaf07.tar.lz
cuberite-49760db89d94ede5d123d927141a6cd60dbaaf07.tar.xz
cuberite-49760db89d94ede5d123d927141a6cd60dbaaf07.tar.zst
cuberite-49760db89d94ede5d123d927141a6cd60dbaaf07.zip
Diffstat (limited to '')
-rw-r--r--src/Protocol/ChunkDataSerializer.cpp176
-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.cpp950
-rw-r--r--src/Protocol/Protocol132.h102
-rw-r--r--src/Protocol/Protocol14x.cpp256
-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.h260
-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, 1744 insertions, 0 deletions
diff --git a/src/Protocol/ChunkDataSerializer.cpp b/src/Protocol/ChunkDataSerializer.cpp
new file mode 100644
index 000000000..78318a5ee
--- /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 "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 9d8183361..9d8183361 100644
--- a/source/Protocol/Protocol.h
+++ b/src/Protocol/Protocol.h
diff --git a/source/Protocol/Protocol125.cpp b/src/Protocol/Protocol125.cpp
index b1dd17ea1..b1dd17ea1 100644
--- a/source/Protocol/Protocol125.cpp
+++ b/src/Protocol/Protocol125.cpp
diff --git a/source/Protocol/Protocol125.h b/src/Protocol/Protocol125.h
index 5a9218f5b..5a9218f5b 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..d8f450588
--- /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 "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..f76272b8d
--- /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 "cryptopp/modes.h"
+#include "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..28122034c
--- /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 "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 746e1c127..746e1c127 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..9c1eaa99a
--- /dev/null
+++ b/src/Protocol/Protocol17x.h
@@ -0,0 +1,260 @@
+
+// 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 "cryptopp/modes.h"
+#include "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 SendExperienceOrb (const cExpOrb & a_ExpOrb) 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 489149d74..489149d74 100644
--- a/source/Protocol/ProtocolRecognizer.cpp
+++ b/src/Protocol/ProtocolRecognizer.cpp
diff --git a/source/Protocol/ProtocolRecognizer.h b/src/Protocol/ProtocolRecognizer.h
index 9ca0c1c88..9ca0c1c88 100644
--- a/source/Protocol/ProtocolRecognizer.h
+++ b/src/Protocol/ProtocolRecognizer.h