Moved source to src

This commit is contained in:
Alexander Harkness
2013-11-24 14:19:41 +00:00
parent 1480d6d64d
commit 675b4aa878
469 changed files with 0 additions and 0 deletions
+176
View File
@@ -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.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);
}
+48
View File
@@ -0,0 +1,48 @@
// ChunkDataSerializer.h
// Interfaces to the cChunkDataSerializer class representing the object that can:
// - serialize chunk data to different protocol versions
// - cache such serialized data for multiple clients
class cChunkDataSerializer
{
protected:
const cChunkDef::BlockTypes & m_BlockTypes;
const cChunkDef::BlockNibbles & m_BlockMetas;
const cChunkDef::BlockNibbles & m_BlockLight;
const cChunkDef::BlockNibbles & m_BlockSkyLight;
const unsigned char * m_BiomeData;
typedef std::map<int, AString> Serializations;
Serializations m_Serializations;
void Serialize29(AString & a_Data); // Release 1.2.4 and 1.2.5
void Serialize39(AString & a_Data); // Release 1.3.1 and 1.3.2
public:
enum
{
RELEASE_1_2_5 = 29,
RELEASE_1_3_2 = 39,
} ;
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
);
const AString & Serialize(int a_Version); // Returns one of the internal m_Serializations[]
} ;
+215
View File
@@ -0,0 +1,215 @@
// Protocol.h
// Interfaces to the cProtocol class representing the generic interface that a protocol
// parser and serializer must implement
#pragma once
#include "../Defines.h"
#include "../Endianness.h"
class cPlayer;
class cEntity;
class cWindow;
class cInventory;
class cPawn;
class cPickup;
class cMonster;
class cChunkDataSerializer;
class cWorld;
class cFallingBlock;
typedef unsigned char Byte;
class cProtocol
{
public:
cProtocol(cClientHandle * a_Client) :
m_Client(a_Client)
{
}
virtual ~cProtocol() {}
/// Called when client sends some data
virtual void DataReceived(const char * a_Data, int a_Size) = 0;
// Sending stuff to clients (alphabetically sorted):
virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) = 0;
virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) = 0;
virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) = 0;
virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0;
virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) = 0;
virtual void SendChat (const AString & a_Message) = 0;
virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) = 0;
virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) = 0;
virtual void SendDestroyEntity (const cEntity & a_Entity) = 0;
virtual void SendDisconnect (const AString & a_Reason) = 0;
virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) = 0; ///< 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) = 0;
virtual void SendEntityHeadLook (const cEntity & a_Entity) = 0;
virtual void SendEntityLook (const cEntity & a_Entity) = 0;
virtual void SendEntityMetadata (const cEntity & a_Entity) = 0;
virtual void SendEntityProperties (const cEntity & a_Entity) = 0;
virtual void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) = 0;
virtual void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) = 0;
virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) = 0;
virtual void SendEntityVelocity (const cEntity & a_Entity) = 0;
virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) = 0;
virtual void SendGameMode (eGameMode a_GameMode) = 0;
virtual void SendHealth (void) = 0;
virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) = 0;
virtual void SendKeepAlive (int a_PingID) = 0;
virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) = 0;
virtual void SendPickupSpawn (const cPickup & a_Pickup) = 0;
virtual void SendPlayerAbilities (void) = 0;
virtual void SendPlayerAnimation (const cPlayer & a_Player, char a_Animation) = 0;
virtual void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline) = 0;
virtual void SendPlayerMaxSpeed (void) = 0; ///< Informs the client of the maximum player speed (1.6.1+)
virtual void SendPlayerMoveLook (void) = 0;
virtual void SendPlayerPosition (void) = 0;
virtual void SendPlayerSpawn (const cPlayer & a_Player) = 0;
virtual void SendRespawn (void) = 0;
virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) = 0; // a_Src coords are Block * 8
virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) = 0;
virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) = 0;
virtual void SendSpawnMob (const cMonster & a_Mob) = 0;
virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) = 0;
virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) = 0;
virtual void SendTabCompletionResults(const AStringVector & a_Results) = 0;
virtual void SendTeleportEntity (const cEntity & a_Entity) = 0;
virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) = 0;
virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) = 0;
virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) = 0;
virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) = 0;
virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) = 0;
virtual void SendWeather (eWeather a_Weather) = 0;
virtual void SendWholeInventory (const cWindow & a_Window) = 0;
virtual void SendWindowClose (const cWindow & a_Window) = 0;
virtual void SendWindowOpen (const cWindow & a_Window) = 0;
virtual void SendWindowProperty (const cWindow & a_Window, short a_Property, short a_Value) = 0;
/// Returns the ServerID used for authentication through session.minecraft.net
virtual AString GetAuthServerID(void) = 0;
protected:
cClientHandle * m_Client;
cCriticalSection m_CSPacket; //< Each SendXYZ() function must acquire this CS in order to send the whole packet at once
/// A generic data-sending routine, all outgoing packet data needs to be routed through this so that descendants may override it
virtual void SendData(const char * a_Data, int a_Size) = 0;
/// Called after writing each packet, enables descendants to flush their buffers
virtual void Flush(void) {};
// Helpers for writing partial packet data, write using SendData()
void WriteByte(Byte a_Value)
{
SendData((const char *)&a_Value, 1);
}
void WriteShort(short a_Value)
{
a_Value = htons(a_Value);
SendData((const char *)&a_Value, 2);
}
/*
void WriteShort(unsigned short a_Value)
{
a_Value = htons(a_Value);
SendData((const char *)&a_Value, 2);
}
*/
void WriteInt(int a_Value)
{
a_Value = htonl(a_Value);
SendData((const char *)&a_Value, 4);
}
void WriteUInt(unsigned int a_Value)
{
a_Value = htonl(a_Value);
SendData((const char *)&a_Value, 4);
}
void WriteInt64 (Int64 a_Value)
{
a_Value = HostToNetwork8(&a_Value);
SendData((const char *)&a_Value, 8);
}
void WriteFloat (float a_Value)
{
unsigned int val = HostToNetwork4(&a_Value);
SendData((const char *)&val, 4);
}
void WriteDouble(double a_Value)
{
unsigned long long val = HostToNetwork8(&a_Value);
SendData((const char *)&val, 8);
}
void WriteString(const AString & a_Value)
{
AString UTF16;
UTF8ToRawBEUTF16(a_Value.c_str(), a_Value.length(), UTF16);
WriteShort((unsigned short)(UTF16.size() / 2));
SendData(UTF16.data(), UTF16.size());
}
void WriteBool(bool a_Value)
{
WriteByte(a_Value ? 1 : 0);
}
void WriteVectorI(const Vector3i & a_Vector)
{
WriteInt(a_Vector.x);
WriteInt(a_Vector.y);
WriteInt(a_Vector.z);
}
void WriteVarInt(UInt32 a_Value)
{
// A 32-bit integer can be encoded by at most 5 bytes:
unsigned char b[5];
int idx = 0;
do
{
b[idx] = (a_Value & 0x7f) | ((a_Value > 0x7f) ? 0x80 : 0x00);
a_Value = a_Value >> 7;
idx++;
} while (a_Value > 0);
SendData((const char *)b, idx);
}
void WriteVarUTF8String(const AString & a_String)
{
WriteVarInt(a_String.size());
SendData(a_String.data(), a_String.size());
}
} ;
File diff suppressed because it is too large Load Diff
+157
View File
@@ -0,0 +1,157 @@
// Protocol125.h
// Interfaces to the cProtocol125 class representing the release 1.2.5 protocol (#29)
#pragma once
#include "Protocol.h"
#include "../ByteBuffer.h"
class cProtocol125 :
public cProtocol
{
typedef cProtocol super;
public:
cProtocol125(cClientHandle * a_Client);
/// Called when client sends some data:
virtual void DataReceived(const char * a_Data, int a_Size) override;
/// Sending stuff to clients (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 {} // This protocol doesn't support such message
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 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;
protected:
/// Results of packet-parsing:
enum {
PARSE_OK = 1,
PARSE_ERROR = -1,
PARSE_UNKNOWN = -2,
PARSE_INCOMPLETE = -3,
} ;
cByteBuffer m_ReceivedData; ///< Buffer for the received data
AString m_Username; ///< Stored in ParseHandshake(), compared to Login username
virtual void SendData(const char * a_Data, int a_Size) override;
/// Sends the Handshake packet
void SendHandshake(const AString & a_ConnectionHash);
/// Parse the packet of the specified type from m_ReceivedData (switch into ParseXYZ() )
virtual int ParsePacket(unsigned char a_PacketType);
// Specific packet parsers:
virtual int ParseArmAnim (void);
virtual int ParseBlockDig (void);
virtual int ParseBlockPlace (void);
virtual int ParseChat (void);
virtual int ParseCreativeInventoryAction(void);
virtual int ParseDisconnect (void);
virtual int ParseEntityAction (void);
virtual int ParseHandshake (void);
virtual int ParseKeepAlive (void);
virtual int ParseLogin (void);
virtual int ParsePing (void);
virtual int ParsePlayerAbilities (void);
virtual int ParsePlayerLook (void);
virtual int ParsePlayerMoveLook (void);
virtual int ParsePlayerOnGround (void);
virtual int ParsePlayerPosition (void);
virtual int ParsePluginMessage (void);
virtual int ParseRespawn (void);
virtual int ParseSlotSelected (void);
virtual int ParseUpdateSign (void);
virtual int ParseUseEntity (void);
virtual int ParseWindowClick (void);
virtual int ParseWindowClose (void);
// Utility functions:
/// Writes a "pre-chunk" packet
void SendPreChunk(int a_ChunkX, int a_ChunkZ, bool a_ShouldLoad);
/// Writes a "set window items" packet with the specified params
void SendWindowSlots(char a_WindowID, int a_NumItems, const cItem * a_Items);
/// Writes one item, "slot" as the protocol wiki calls it
virtual void WriteItem(const cItem & a_Item);
/// Parses one item, "slot" as the protocol wiki calls it, from m_ReceivedData; returns the usual ParsePacket() codes
virtual int ParseItem(cItem & a_Item);
/// Writes the COMMON entity metadata
void WriteCommonMetadata(const cEntity & a_Entity);
/// Writes normal entity metadata
void WriteEntityMetadata(const cEntity & a_Entity);
/// Writes mobile entity metadata
void WriteMobMetadata(const cMonster & a_Mob);
} ;
+950
View File
@@ -0,0 +1,950 @@
// Protocol132.cpp
// Implements the cProtocol132 class representing the release 1.3.2 protocol (#39)
#include "Globals.h"
#include "Protocol132.h"
#include "../Root.h"
#include "../Server.h"
#include "../World.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 "../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);
}
+102
View File
@@ -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);
} ;
+256
View File
@@ -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();
}
+63
View File
@@ -0,0 +1,63 @@
// Protocol14x.h
/*
Interfaces to 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)
*/
#pragma once
#include "Protocol132.h"
class cProtocol142 :
public cProtocol132
{
typedef cProtocol132 super;
public:
cProtocol142(cClientHandle * a_Client);
// Sending commands (alphabetically sorted):
virtual void SendPickupSpawn (const cPickup & a_Pickup) override;
virtual void SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override;
virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) override;
// Specific packet parsers:
virtual int ParseLocaleViewDistance(void) override;
} ;
class cProtocol146 :
public cProtocol142
{
typedef cProtocol142 super;
public:
cProtocol146(cClientHandle * a_Client);
virtual void SendPickupSpawn (const cPickup & a_Pickup) override;
virtual void SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock) 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;
} ;
+138
View File
@@ -0,0 +1,138 @@
// Protocol15x.cpp
/*
Implements the 1.5.x protocol classes:
- cProtocol150
- release 1.5 protocol (#60)
- release 1.5.2 protocol (#61, no relevant changes found)
*/
#include "Globals.h"
#include "Protocol15x.h"
#include "../ClientHandle.h"
#include "../Item.h"
#include "../UI/Window.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_WINDOW_OPEN = 0x64,
} ;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cProtocol150:
cProtocol150::cProtocol150(cClientHandle * a_Client) :
super(a_Client)
{
}
void cProtocol150::SendWindowOpen(const cWindow & a_Window)
{
if (a_Window.GetWindowType() < 0)
{
// Do not send for inventory windows
return;
}
cCSLock Lock(m_CSPacket);
WriteByte (PACKET_WINDOW_OPEN);
WriteByte (a_Window.GetWindowID());
WriteByte (a_Window.GetWindowType());
WriteString(a_Window.GetWindowTitle());
WriteByte (a_Window.GetNumNonInventorySlots());
WriteByte (1); // Use title
Flush();
}
int cProtocol150::ParseWindowClick(void)
{
HANDLE_PACKET_READ(ReadChar, char, WindowID);
HANDLE_PACKET_READ(ReadBEShort, short, SlotNum);
HANDLE_PACKET_READ(ReadByte, Byte, Button);
HANDLE_PACKET_READ(ReadBEShort, short, TransactionID);
HANDLE_PACKET_READ(ReadByte, Byte, Mode);
cItem HeldItem;
int res = ParseItem(HeldItem);
if (res < 0)
{
return res;
}
// Convert Button, Mode, SlotNum and HeldItem into eClickAction:
eClickAction Action;
switch ((Mode << 8) | Button)
{
case 0x0000: Action = (SlotNum != -999) ? caLeftClick : caLeftClickOutside; break;
case 0x0001: Action = (SlotNum != -999) ? caRightClick : caRightClickOutside; break;
case 0x0100: Action = caShiftLeftClick; break;
case 0x0101: Action = caShiftRightClick; break;
case 0x0200: Action = caNumber1; break;
case 0x0201: Action = caNumber2; break;
case 0x0202: Action = caNumber3; break;
case 0x0203: Action = caNumber4; break;
case 0x0204: Action = caNumber5; break;
case 0x0205: Action = caNumber6; break;
case 0x0206: Action = caNumber7; break;
case 0x0207: Action = caNumber8; break;
case 0x0208: Action = caNumber9; break;
case 0x0300: Action = caMiddleClick; break;
case 0x0400: Action = (SlotNum == -999) ? caLeftClickOutsideHoldNothing : caDropKey; break;
case 0x0401: Action = (SlotNum == -999) ? caRightClickOutsideHoldNothing : caCtrlDropKey; break;
case 0x0500: Action = (SlotNum == -999) ? caLeftPaintBegin : caUnknown; break;
case 0x0501: Action = (SlotNum != -999) ? caLeftPaintProgress : caUnknown; break;
case 0x0502: Action = (SlotNum == -999) ? caLeftPaintEnd : caUnknown; break;
case 0x0504: Action = (SlotNum == -999) ? caRightPaintBegin : caUnknown; break;
case 0x0505: Action = (SlotNum != -999) ? caRightPaintProgress : caUnknown; break;
case 0x0506: Action = (SlotNum == -999) ? caRightPaintEnd : caUnknown; break;
case 0x0600: Action = caDblClick; break;
}
if (Action == caUnknown)
{
LOGWARNING("Received an unknown click action combination: Mode = %d, Button = %d, Slot = %d, HeldItem = %s. Ignoring packet.",
Mode, Button, SlotNum, ItemToFullString(HeldItem).c_str()
);
ASSERT(!"Unknown click action");
return PARSE_OK;
}
m_Client->HandleWindowClick(WindowID, SlotNum, Action, HeldItem);
return PARSE_OK;
}
+38
View File
@@ -0,0 +1,38 @@
// Protocol15x.h
/*
Declares the 1.5.x protocol classes:
- cProtocol150
- release 1.5 and 1.5.1 protocol (#60)
- release 1.5.2 protocol (#61; no relevant changes found)
*/
#pragma once
#include "Protocol14x.h"
class cProtocol150 :
public cProtocol146
{
typedef cProtocol146 super;
public:
cProtocol150(cClientHandle * a_Client);
virtual void SendWindowOpen(const cWindow & a_Window) override;
virtual int ParseWindowClick(void);
} ;
+268
View File
@@ -0,0 +1,268 @@
// Protocol16x.cpp
/*
Implements the 1.6.x protocol classes:
- cProtocol161
- release 1.6.1 protocol (#73)
- cProtocol162
- release 1.6.2 protocol (#74)
- release 1.6.3 protocol (#77) - no relevant changes
- release 1.6.4 protocol (#78) - no relevant changes
(others may be added later in the future for the 1.6 release series)
*/
#include "Globals.h"
#include "Protocol16x.h"
#include "../ClientHandle.h"
#include "../Entities/Entity.h"
#include "../Entities/Player.h"
#include "../UI/Window.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_CHAT = 0x03,
PACKET_UPDATE_HEALTH = 0x08,
PACKET_STEER_VEHICLE = 0x1b,
PACKET_ATTACH_ENTITY = 0x27,
PACKET_ENTITY_PROPERTIES = 0x2c,
PACKET_WINDOW_OPEN = 0x64,
PACKET_TILE_EDITOR_OPEN = 0x85,
PACKET_PLAYER_ABILITIES = 0xca,
} ;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cProtocol161:
cProtocol161::cProtocol161(cClientHandle * a_Client) :
super(a_Client)
{
}
void cProtocol161::SendAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle)
{
cCSLock Lock(m_CSPacket);
WriteByte(PACKET_ATTACH_ENTITY);
WriteInt(a_Entity.GetUniqueID());
WriteInt((a_Vehicle == NULL) ? -1 : a_Vehicle->GetUniqueID());
WriteBool(false); // TODO: "Should use leash?" -> no
Flush();
}
void cProtocol161::SendChat(const AString & a_Message)
{
super::SendChat(Printf("{\"text\":\"%s\"}", EscapeString(a_Message).c_str()));
}
void cProtocol161::SendEditSign(int a_BlockX, int a_BlockY, int a_BlockZ)
{
cCSLock Lock(m_CSPacket);
WriteByte(PACKET_TILE_EDITOR_OPEN);
WriteByte(0);
WriteInt(a_BlockX);
WriteInt(a_BlockY);
WriteInt(a_BlockZ);
Flush();
}
void cProtocol161::SendGameMode(eGameMode a_GameMode)
{
super::SendGameMode(a_GameMode);
SendPlayerMaxSpeed();
}
void cProtocol161::SendHealth(void)
{
cCSLock Lock(m_CSPacket);
WriteByte (PACKET_UPDATE_HEALTH);
WriteFloat((float)m_Client->GetPlayer()->GetHealth());
WriteShort(m_Client->GetPlayer()->GetFoodLevel());
WriteFloat((float)m_Client->GetPlayer()->GetFoodSaturationLevel());
Flush();
}
void cProtocol161::SendPlayerMaxSpeed(void)
{
cCSLock Lock(m_CSPacket);
WriteByte(PACKET_ENTITY_PROPERTIES);
WriteInt(m_Client->GetPlayer()->GetUniqueID());
WriteInt(1);
WriteString("generic.movementSpeed");
WriteDouble(m_Client->GetPlayer()->GetMaxSpeed());
Flush();
}
void cProtocol161::SendRespawn(void)
{
// Besides sending the respawn, we need to also send the player max speed, otherwise the client reverts to super-fast
super::SendRespawn();
SendPlayerMaxSpeed();
}
void cProtocol161::SendWindowOpen(const cWindow & a_Window)
{
if (a_Window.GetWindowType() < 0)
{
// Do not send for inventory windows
return;
}
cCSLock Lock(m_CSPacket);
WriteByte (PACKET_WINDOW_OPEN);
WriteByte (a_Window.GetWindowID());
WriteByte (a_Window.GetWindowType());
WriteString(a_Window.GetWindowTitle());
WriteByte (a_Window.GetNumNonInventorySlots());
WriteByte (1); // Use title
if (a_Window.GetWindowType() == cWindow::wtAnimalChest)
{
WriteInt(0); // TODO: The animal's EntityID
}
Flush();
}
int cProtocol161::ParseEntityAction(void)
{
HANDLE_PACKET_READ(ReadBEInt, int, EntityID);
HANDLE_PACKET_READ(ReadChar, char, ActionID);
HANDLE_PACKET_READ(ReadBEInt, int, UnknownHorseVal);
m_Client->HandleEntityAction(EntityID, ActionID);
return PARSE_OK;
}
int cProtocol161::ParsePlayerAbilities(void)
{
HANDLE_PACKET_READ(ReadByte, Byte, Flags);
HANDLE_PACKET_READ(ReadBEFloat, float, FlyingSpeed);
HANDLE_PACKET_READ(ReadBEFloat, float, WalkingSpeed);
// TODO: m_Client->HandlePlayerAbilities(...);
return PARSE_OK;
}
int cProtocol161::ParseSteerVehicle(void)
{
HANDLE_PACKET_READ(ReadBEFloat, float, Sideways);
HANDLE_PACKET_READ(ReadBEFloat, float, Forward);
HANDLE_PACKET_READ(ReadBool, bool, Jump);
HANDLE_PACKET_READ(ReadBool, bool, Unmount);
if (Unmount)
{
m_Client->HandleUnmount();
}
else
{
m_Client->HandleSteerVehicle(Forward, Sideways);
}
return PARSE_OK;
}
int cProtocol161::ParsePacket(unsigned char a_PacketType)
{
switch (a_PacketType)
{
case PACKET_STEER_VEHICLE: return ParseSteerVehicle();
default: return super::ParsePacket(a_PacketType);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cProtocol162:
cProtocol162::cProtocol162(cClientHandle * a_Client) :
super(a_Client)
{
}
void cProtocol162::SendPlayerMaxSpeed(void)
{
cCSLock Lock(m_CSPacket);
WriteByte(PACKET_ENTITY_PROPERTIES);
WriteInt(m_Client->GetPlayer()->GetUniqueID());
WriteInt(1);
WriteString("generic.movementSpeed");
WriteDouble(m_Client->GetPlayer()->GetMaxSpeed());
WriteShort(0);
Flush();
}
+76
View File
@@ -0,0 +1,76 @@
// Protocol16x.h
/*
Declares the 1.6.x protocol classes:
- cProtocol161
- release 1.6.1 protocol (#73)
- cProtocol162
- release 1.6.2 protocol (#74)
- release 1.6.3 protocol (#77) - no relevant changes
- release 1.6.4 protocol (#78) - no relevant changes
(others may be added later in the future for the 1.6 release series)
*/
#pragma once
#include "Protocol15x.h"
class cProtocol161 :
public cProtocol150
{
typedef cProtocol150 super;
public:
cProtocol161(cClientHandle * a_Client);
protected:
// cProtocol150 overrides:
virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override;
virtual void SendChat (const AString & a_Message) 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 SendGameMode (eGameMode a_GameMode) override;
virtual void SendHealth (void) override;
virtual void SendPlayerMaxSpeed(void) override;
virtual void SendRespawn (void) override;
virtual void SendWindowOpen (const cWindow & a_Window) override;
virtual int ParseEntityAction (void) override;
virtual int ParsePlayerAbilities(void) override;
// New packets:
virtual int ParseSteerVehicle(void);
// Enable new packets' handling
virtual int ParsePacket(unsigned char a_PacketType) override;
} ;
class cProtocol162 :
public cProtocol161
{
typedef cProtocol161 super;
public:
cProtocol162(cClientHandle * a_Client);
protected:
// cProtocol161 overrides:
virtual void SendPlayerMaxSpeed(void) override;
} ;
File diff suppressed because it is too large Load Diff
+258
View File
@@ -0,0 +1,258 @@
// 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 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);
} ;
+905
View File
@@ -0,0 +1,905 @@
// ProtocolRecognizer.cpp
// Implements the cProtocolRecognizer class representing the meta-protocol that recognizes possibly multiple
// protocol versions and redirects everything to them
#include "Globals.h"
#include "ProtocolRecognizer.h"
#include "Protocol125.h"
#include "Protocol132.h"
#include "Protocol14x.h"
#include "Protocol15x.h"
#include "Protocol16x.h"
#include "Protocol17x.h"
#include "../ClientHandle.h"
#include "../Root.h"
#include "../Server.h"
#include "../World.h"
#include "../ChatColor.h"
cProtocolRecognizer::cProtocolRecognizer(cClientHandle * a_Client) :
super(a_Client),
m_Protocol(NULL),
m_Buffer(512)
{
}
cProtocolRecognizer::~cProtocolRecognizer()
{
delete m_Protocol;
}
AString cProtocolRecognizer::GetVersionTextFromInt(int a_ProtocolVersion)
{
switch (a_ProtocolVersion)
{
case PROTO_VERSION_1_2_5: return "1.2.5";
case PROTO_VERSION_1_3_2: return "1.3.2";
case PROTO_VERSION_1_4_2: return "1.4.2";
case PROTO_VERSION_1_4_4: return "1.4.4";
case PROTO_VERSION_1_4_6: return "1.4.6";
case PROTO_VERSION_1_5_0: return "1.5";
case PROTO_VERSION_1_5_2: return "1.5.2";
case PROTO_VERSION_1_6_1: return "1.6.1";
case PROTO_VERSION_1_6_2: return "1.6.2";
case PROTO_VERSION_1_6_3: return "1.6.3";
case PROTO_VERSION_1_6_4: return "1.6.4";
case PROTO_VERSION_1_7_2: return "1.7.2";
}
ASSERT(!"Unknown protocol version");
return Printf("Unknown protocol (%d)", a_ProtocolVersion);
}
void cProtocolRecognizer::DataReceived(const char * a_Data, int a_Size)
{
if (m_Protocol == NULL)
{
if (!m_Buffer.Write(a_Data, a_Size))
{
m_Client->Kick("Unsupported protocol version");
return;
}
if (!TryRecognizeProtocol())
{
return;
}
// The protocol has just been recognized, dump the whole m_Buffer contents into it for parsing:
AString Dump;
m_Buffer.ResetRead();
m_Buffer.ReadAll(Dump);
m_Protocol->DataReceived(Dump.data(), Dump.size());
}
else
{
m_Protocol->DataReceived(a_Data, a_Size);
}
}
void cProtocolRecognizer::SendAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendAttachEntity(a_Entity, a_Vehicle);
}
void cProtocolRecognizer::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendBlockAction(a_BlockX, a_BlockY, a_BlockZ, a_Byte1, a_Byte2, a_BlockType);
}
void cProtocolRecognizer::SendBlockBreakAnim(int a_entityID, int a_BlockX, int a_BlockY, int a_BlockZ, char stage)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendBlockBreakAnim(a_entityID, a_BlockX, a_BlockY, a_BlockZ, stage);
}
void cProtocolRecognizer::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendBlockChange(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta);
}
void cProtocolRecognizer::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendBlockChanges(a_ChunkX, a_ChunkZ, a_Changes);
}
void cProtocolRecognizer::SendChat(const AString & a_Message)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendChat(a_Message);
}
void cProtocolRecognizer::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendChunkData(a_ChunkX, a_ChunkZ, a_Serializer);
}
void cProtocolRecognizer::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendCollectPickup(a_Pickup, a_Player);
}
void cProtocolRecognizer::SendDestroyEntity(const cEntity & a_Entity)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendDestroyEntity(a_Entity);
}
void cProtocolRecognizer::SendDisconnect(const AString & a_Reason)
{
if (m_Protocol != NULL)
{
m_Protocol->SendDisconnect(a_Reason);
}
else
{
// This is used when the client sends a server-ping, respond with the default packet:
WriteByte ((char)0xff); // PACKET_DISCONNECT
WriteString(a_Reason);
}
}
void cProtocolRecognizer::SendEditSign(int a_BlockX, int a_BlockY, int a_BlockZ)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendEditSign(a_BlockX, a_BlockY, a_BlockZ);
}
void cProtocolRecognizer::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendEntityEquipment(a_Entity, a_SlotNum, a_Item);
}
void cProtocolRecognizer::SendEntityHeadLook(const cEntity & a_Entity)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendEntityHeadLook(a_Entity);
}
void cProtocolRecognizer::SendEntityLook(const cEntity & a_Entity)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendEntityLook(a_Entity);
}
void cProtocolRecognizer::SendEntityMetadata(const cEntity & a_Entity)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendEntityMetadata(a_Entity);
}
void cProtocolRecognizer::SendEntityProperties(const cEntity & a_Entity)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendEntityProperties(a_Entity);
}
void cProtocolRecognizer::SendEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendEntityRelMove(a_Entity, a_RelX, a_RelY, a_RelZ);
}
void cProtocolRecognizer::SendEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendEntityRelMoveLook(a_Entity, a_RelX, a_RelY, a_RelZ);
}
void cProtocolRecognizer::SendEntityStatus(const cEntity & a_Entity, char a_Status)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendEntityStatus(a_Entity, a_Status);
}
void cProtocolRecognizer::SendEntityVelocity(const cEntity & a_Entity)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendEntityVelocity(a_Entity);
}
void cProtocolRecognizer::SendExplosion(double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendExplosion(a_BlockX,a_BlockY,a_BlockZ,a_Radius, a_BlocksAffected, a_PlayerMotion);
}
void cProtocolRecognizer::SendGameMode(eGameMode a_GameMode)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendGameMode(a_GameMode);
}
void cProtocolRecognizer::SendHealth(void)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendHealth();
}
void cProtocolRecognizer::SendWindowProperty(const cWindow & a_Window, short a_Property, short a_Value)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendWindowProperty(a_Window, a_Property, a_Value);
}
void cProtocolRecognizer::SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem & a_Item)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendInventorySlot(a_WindowID, a_SlotNum, a_Item);
}
void cProtocolRecognizer::SendKeepAlive(int a_PingID)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendKeepAlive(a_PingID);
}
void cProtocolRecognizer::SendLogin(const cPlayer & a_Player, const cWorld & a_World)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendLogin(a_Player, a_World);
}
void cProtocolRecognizer::SendPickupSpawn(const cPickup & a_Pickup)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendPickupSpawn(a_Pickup);
}
void cProtocolRecognizer::SendPlayerAbilities(void)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendPlayerAbilities();
}
void cProtocolRecognizer::SendPlayerAnimation(const cPlayer & a_Player, char a_Animation)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendPlayerAnimation(a_Player, a_Animation);
}
void cProtocolRecognizer::SendPlayerListItem(const cPlayer & a_Player, bool a_IsOnline)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendPlayerListItem(a_Player, a_IsOnline);
}
void cProtocolRecognizer::SendPlayerMaxSpeed(void)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendPlayerMaxSpeed();
}
void cProtocolRecognizer::SendPlayerMoveLook(void)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendPlayerMoveLook();
}
void cProtocolRecognizer::SendPlayerPosition(void)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendPlayerPosition();
}
void cProtocolRecognizer::SendPlayerSpawn(const cPlayer & a_Player)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendPlayerSpawn(a_Player);
}
void cProtocolRecognizer::SendRespawn(void)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendRespawn();
}
void cProtocolRecognizer::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch);
}
void cProtocolRecognizer::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendSoundParticleEffect(a_EffectID, a_SrcX, a_SrcY, a_SrcZ, a_Data);
}
void cProtocolRecognizer::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendSpawnFallingBlock(a_FallingBlock);
}
void cProtocolRecognizer::SendSpawnMob(const cMonster & a_Mob)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendSpawnMob(a_Mob);
}
void cProtocolRecognizer::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendSpawnObject(a_Entity, a_ObjectType, a_ObjectData, a_Yaw, a_Pitch);
}
void cProtocolRecognizer::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendSpawnVehicle(a_Vehicle, a_VehicleType, a_VehicleSubType);
}
void cProtocolRecognizer::SendTabCompletionResults(const AStringVector & a_Results)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendTabCompletionResults(a_Results);
}
void cProtocolRecognizer::SendTeleportEntity(const cEntity & a_Entity)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendTeleportEntity(a_Entity);
}
void cProtocolRecognizer::SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendThunderbolt(a_BlockX, a_BlockY, a_BlockZ);
}
void cProtocolRecognizer::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendTimeUpdate(a_WorldAge, a_TimeOfDay);
}
void cProtocolRecognizer::SendUnloadChunk(int a_ChunkX, int a_ChunkZ)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendUnloadChunk(a_ChunkX, a_ChunkZ);
}
void cProtocolRecognizer::SendUpdateSign(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendUpdateSign(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4);
}
void cProtocolRecognizer::SendUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ )
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendUseBed(a_Entity, a_BlockX, a_BlockY, a_BlockZ);
}
void cProtocolRecognizer::SendWeather(eWeather a_Weather)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendWeather(a_Weather);
}
void cProtocolRecognizer::SendWholeInventory(const cWindow & a_Window)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendWholeInventory(a_Window);
}
void cProtocolRecognizer::SendWindowClose(const cWindow & a_Window)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendWindowClose(a_Window);
}
void cProtocolRecognizer::SendWindowOpen(const cWindow & a_Window)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendWindowOpen(a_Window);
}
AString cProtocolRecognizer::GetAuthServerID(void)
{
ASSERT(m_Protocol != NULL);
return m_Protocol->GetAuthServerID();
}
void cProtocolRecognizer::SendData(const char * a_Data, int a_Size)
{
// This is used only when handling the server ping
m_Client->SendData(a_Data, a_Size);
}
bool cProtocolRecognizer::TryRecognizeProtocol(void)
{
// NOTE: If a new protocol is added or an old one is removed, adjust MCS_CLIENT_VERSIONS and
// MCS_PROTOCOL_VERSIONS macros in the header file, as well as PROTO_VERSION_LATEST macro
// The first packet should be a Handshake, 0x02:
unsigned char PacketType;
if (!m_Buffer.ReadByte(PacketType))
{
return false;
}
switch (PacketType)
{
case 0x02: return TryRecognizeLengthlessProtocol(); // Handshake, continue recognizing
case 0xfe:
{
// This may be either a packet length or the length-less Ping packet
Byte NextByte;
if (!m_Buffer.ReadByte(NextByte))
{
// Not enough data for either protocol
// This could actually happen with the 1.2 / 1.3 client, but their support is fading out anyway
return false;
}
if (NextByte != 0x01)
{
// This is definitely NOT a length-less Ping packet, handle as lengthed protocol:
break;
}
if (!m_Buffer.ReadByte(NextByte))
{
// There is no more data. Although this *could* mean TCP fragmentation, it is highly unlikely
// and rather this is a 1.4 client sending a regular Ping packet (without the following Plugin message)
SendLengthlessServerPing();
return false;
}
if (NextByte == 0xfa)
{
// Definitely a length-less Ping followed by a Plugin message
SendLengthlessServerPing();
return false;
}
// Definitely a lengthed Initial handshake, handle below:
break;
}
} // switch (PacketType)
// This must be a lengthed protocol, try if it has the entire initial handshake packet:
m_Buffer.ResetRead();
UInt32 PacketLen;
UInt32 ReadSoFar = m_Buffer.GetReadableSpace();
if (!m_Buffer.ReadVarInt(PacketLen))
{
// Not enough bytes for the packet length, keep waiting
return false;
}
ReadSoFar -= m_Buffer.GetReadableSpace();
if (!m_Buffer.CanReadBytes(PacketLen))
{
// Not enough bytes for the packet, keep waiting
return false;
}
return TryRecognizeLengthedProtocol(PacketLen - ReadSoFar);
}
bool cProtocolRecognizer::TryRecognizeLengthlessProtocol(void)
{
// The comm started with 0x02, which is a Handshake packet in the length-less protocol family
// 1.3.2 starts with 0x02 0x39 <name-length-short>
// 1.2.5 starts with 0x02 <name-length-short> and name is expected to less than 0x3900 long :)
char ch;
if (!m_Buffer.ReadChar(ch))
{
return false;
}
switch (ch)
{
case PROTO_VERSION_1_3_2:
{
m_Protocol = new cProtocol132(m_Client);
return true;
}
case PROTO_VERSION_1_4_2:
case PROTO_VERSION_1_4_4:
{
m_Protocol = new cProtocol142(m_Client);
return true;
}
case PROTO_VERSION_1_4_6:
{
m_Protocol = new cProtocol146(m_Client);
return true;
}
case PROTO_VERSION_1_5_0:
case PROTO_VERSION_1_5_2:
{
m_Protocol = new cProtocol150(m_Client);
return true;
}
case PROTO_VERSION_1_6_1:
{
m_Protocol = new cProtocol161(m_Client);
return true;
}
case PROTO_VERSION_1_6_2:
case PROTO_VERSION_1_6_3:
case PROTO_VERSION_1_6_4:
{
m_Protocol = new cProtocol162(m_Client);
return true;
}
}
m_Protocol = new cProtocol125(m_Client);
return true;
}
bool cProtocolRecognizer::TryRecognizeLengthedProtocol(UInt32 a_PacketLengthRemaining)
{
UInt32 PacketType;
UInt32 NumBytesRead = m_Buffer.GetReadableSpace();
if (!m_Buffer.ReadVarInt(PacketType))
{
return false;
}
if (PacketType != 0x00)
{
// Not an initial handshake packet, we don't know how to talk to them
LOGINFO("Client \"%s\" uses an unsupported protocol (lengthed, initial packet %u)",
m_Client->GetIPString().c_str(), PacketType
);
m_Client->Kick("Unsupported protocol version");
return false;
}
UInt32 ProtocolVersion;
if (!m_Buffer.ReadVarInt(ProtocolVersion))
{
return false;
}
NumBytesRead -= m_Buffer.GetReadableSpace();
switch (ProtocolVersion)
{
case PROTO_VERSION_1_7_2:
{
AString ServerAddress;
short ServerPort;
UInt32 NextState;
m_Buffer.ReadVarUTF8String(ServerAddress);
m_Buffer.ReadBEShort(ServerPort);
m_Buffer.ReadVarInt(NextState);
m_Buffer.CommitRead();
m_Protocol = new cProtocol172(m_Client, ServerAddress, ServerPort, NextState);
return true;
}
}
LOGINFO("Client \"%s\" uses an unsupported protocol (lengthed, version %u)",
m_Client->GetIPString().c_str(), ProtocolVersion
);
m_Client->Kick("Unsupported protocol version");
return false;
}
void cProtocolRecognizer::SendLengthlessServerPing(void)
{
AString Reply;
switch (cRoot::Get()->GetPrimaryServerVersion())
{
case PROTO_VERSION_1_2_5:
case PROTO_VERSION_1_3_2:
{
// http://wiki.vg/wiki/index.php?title=Protocol&oldid=3099#Server_List_Ping_.280xFE.29
Printf(Reply, "%s%s%i%s%i",
cRoot::Get()->GetServer()->GetDescription().c_str(),
cChatColor::Delimiter.c_str(),
cRoot::Get()->GetServer()->GetNumPlayers(),
cChatColor::Delimiter.c_str(),
cRoot::Get()->GetServer()->GetMaxPlayers()
);
break;
}
case PROTO_VERSION_1_4_2:
case PROTO_VERSION_1_4_4:
case PROTO_VERSION_1_4_6:
case PROTO_VERSION_1_5_0:
case PROTO_VERSION_1_5_2:
case PROTO_VERSION_1_6_1:
case PROTO_VERSION_1_6_2:
case PROTO_VERSION_1_6_3:
case PROTO_VERSION_1_6_4:
{
// The server list ping now has 1 more byte of "magic". Mojang just loves to complicate stuff.
// http://wiki.vg/wiki/index.php?title=Protocol&oldid=3101#Server_List_Ping_.280xFE.29
// _X 2012_10_31: I know that this needn't eat the byte, since it still may be in transit.
// Who cares? We're disconnecting anyway.
m_Buffer.ResetRead();
if (m_Buffer.CanReadBytes(2))
{
byte val;
m_Buffer.ReadByte(val); // Packet type - Serverlist ping
m_Buffer.ReadByte(val); // 0x01 magic value
ASSERT(val == 0x01);
}
// http://wiki.vg/wiki/index.php?title=Server_List_Ping&oldid=3100
AString NumPlayers;
Printf(NumPlayers, "%d", cRoot::Get()->GetServer()->GetNumPlayers());
AString MaxPlayers;
Printf(MaxPlayers, "%d", cRoot::Get()->GetServer()->GetMaxPlayers());
AString ProtocolVersionNum;
Printf(ProtocolVersionNum, "%d", cRoot::Get()->GetPrimaryServerVersion());
AString ProtocolVersionTxt(GetVersionTextFromInt(cRoot::Get()->GetPrimaryServerVersion()));
// Cannot use Printf() because of in-string NUL bytes.
Reply = cChatColor::Delimiter;
Reply.append("1");
Reply.push_back(0);
Reply.append(ProtocolVersionNum);
Reply.push_back(0);
Reply.append(ProtocolVersionTxt);
Reply.push_back(0);
Reply.append(cRoot::Get()->GetServer()->GetDescription());
Reply.push_back(0);
Reply.append(NumPlayers);
Reply.push_back(0);
Reply.append(MaxPlayers);
break;
}
} // switch (m_PrimaryServerVersion)
m_Client->Kick(Reply);
}
+152
View File
@@ -0,0 +1,152 @@
// ProtocolRecognizer.h
// Interfaces to the cProtocolRecognizer class representing the meta-protocol that recognizes possibly multiple
// protocol versions and redirects everything to them
#pragma once
#include "Protocol.h"
#include "../ByteBuffer.h"
// Adjust these if a new protocol is added or an old one is removed:
#define MCS_CLIENT_VERSIONS "1.2.4, 1.2.5, 1.3.1, 1.3.2, 1.4.2, 1.4.4, 1.4.5, 1.4.6, 1.4.7, 1.5, 1.5.1, 1.5.2, 1.6.1, 1.6.2, 1.6.3, 1.6.4, 1.7.2"
#define MCS_PROTOCOL_VERSIONS "29, 39, 47, 49, 51, 60, 61, 73, 74, 77, 78, 4"
class cProtocolRecognizer :
public cProtocol
{
typedef cProtocol super;
public:
enum
{
PROTO_VERSION_1_2_5 = 29,
PROTO_VERSION_1_3_2 = 39,
PROTO_VERSION_1_4_2 = 47,
PROTO_VERSION_1_4_4 = 49,
PROTO_VERSION_1_4_6 = 51,
PROTO_VERSION_1_5_0 = 60,
PROTO_VERSION_1_5_2 = 61,
PROTO_VERSION_1_6_1 = 73,
PROTO_VERSION_1_6_2 = 74,
PROTO_VERSION_1_6_3 = 77,
PROTO_VERSION_1_6_4 = 78,
PROTO_VERSION_NEXT,
PROTO_VERSION_LATEST = PROTO_VERSION_NEXT - 1, ///< Automatically assigned to the last protocol version, this serves as the default for PrimaryServerVersion
// These will be kept "under" the next / latest, because the next and latest are only needed for previous protocols
PROTO_VERSION_1_7_2 = 4,
} ;
cProtocolRecognizer(cClientHandle * a_Client);
virtual ~cProtocolRecognizer();
/// Translates protocol version number into protocol version text: 49 -> "1.4.4"
static AString GetVersionTextFromInt(int a_ProtocolVersion);
/// Called when client sends some data:
virtual void DataReceived(const char * a_Data, 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;
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;
virtual void SendData(const char * a_Data, int a_Size) override;
protected:
cProtocol * m_Protocol; //< The recognized protocol
cByteBuffer m_Buffer; //< Buffer for the incoming data until we recognize the protocol
/// Tries to recognize protocol based on m_Buffer contents; returns true if recognized
bool TryRecognizeProtocol(void);
/** Tries to recognize a protocol in the length-less family, based on m_Buffer; returns true if recognized.
Handles protocols before release 1.7, that didn't include packet lengths, and started with a 0x02 handshake packet
Note that length-less server ping is handled directly in TryRecognizeProtocol(), this function is called only
when the 0x02 Handshake packet has been received
*/
bool TryRecognizeLengthlessProtocol(void);
/** Tries to recognize a protocol in the leghted family (1.7+), based on m_Buffer; returns true if recognized.
The packet length and type have already been read, type is 0
The number of bytes remaining in the packet is passed as a_PacketLengthRemaining
**/
bool TryRecognizeLengthedProtocol(UInt32 a_PacketLengthRemaining);
/** Called when the recognizer gets a length-less protocol's server ping packet
Responds with server stats and destroys the client.
*/
void SendLengthlessServerPing(void);
} ;