2012-08-30 02:56:59 -07:00
// Protocol132.cpp
// Implements the cProtocol132 class representing the release 1.3.2 protocol (#39)
# include "Globals.h"
# include "Protocol132.h"
2012-09-23 15:09:57 -07:00
# include "../Root.h"
# include "../Server.h"
# include "../ClientHandle.h"
2012-09-23 13:03:26 -07:00
# include "../../CryptoPP/randpool.h"
2012-09-23 15:09:57 -07:00
# include "../Item.h"
2012-09-23 14:37:46 -07:00
# include "ChunkDataSerializer.h"
2012-09-23 15:09:57 -07:00
# include "../Player.h"
2012-09-23 13:53:08 -07:00
# include "../Mobs/Monster.h"
2012-09-23 13:03:26 -07:00
# include "../UI/Window.h"
2012-10-03 11:44:42 -07:00
# include "../Pickup.h"
2012-08-30 02:56:59 -07:00
# define HANDLE_PACKET_READ(Proc, Type, Var) \
Type Var ; \
{ \
if ( ! m_ReceivedData . Proc ( Var ) ) \
{ \
2012-11-20 12:44:47 -08:00
m_ReceivedData . CheckValid ( ) ; \
2012-08-30 02:56:59 -07:00
return PARSE_INCOMPLETE ; \
} \
2012-11-20 12:44:47 -08:00
m_ReceivedData . CheckValid ( ) ; \
2012-08-30 02:56:59 -07:00
}
typedef unsigned char Byte ;
2012-08-30 14:06:13 -07:00
using namespace CryptoPP ;
const int MAX_ENC_LEN = 512 ; // Maximum size of the encrypted message; should be 128, but who knows...
2012-08-31 14:59:57 -07:00
enum
{
2012-10-21 00:46:28 -07:00
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
2012-08-31 14:59:57 -07:00
} ;
2012-09-06 10:36:59 -07:00
// 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 ;
*/
2012-08-30 02:56:59 -07:00
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cProtocol132:
cProtocol132 : : cProtocol132 ( cClientHandle * a_Client ) :
2012-08-30 14:06:13 -07:00
super ( a_Client ) ,
m_IsEncrypted ( false )
2012-08-30 02:56:59 -07:00
{
}
2012-08-31 14:59:57 -07:00
cProtocol132 : : ~ cProtocol132 ( )
{
if ( ! m_DataToSend . empty ( ) )
{
LOGD ( " There are %d unsent bytes while deleting cProtocol132 " , m_DataToSend . size ( ) ) ;
}
}
2012-08-30 02:56:59 -07:00
void cProtocol132 : : DataReceived ( const char * a_Data , int a_Size )
{
2012-08-30 14:06:13 -07:00
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 ) ;
}
}
2012-08-31 14:59:57 -07:00
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 ( ) ;
}
2012-10-21 01:01:59 -07:00
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 ( ) ;
}
2012-08-31 14:59:57 -07:00
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 ( ) ;
}
2012-10-03 11:44:42 -07:00
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 )
) ;
}
2012-08-31 14:59:57 -07:00
void cProtocol132 : : SendDestroyEntity ( const cEntity & a_Entity )
{
2012-10-20 03:36:50 -07:00
if ( a_Entity . GetUniqueID ( ) = = m_Client - > GetPlayer ( ) - > GetUniqueID ( ) )
{
// Do not send "destroy self" to the client, the client would crash (FS #254)
return ;
}
2012-08-31 14:59:57 -07:00
cCSLock Lock ( m_CSPacket ) ;
WriteByte ( PACKET_DESTROY_ENTITIES ) ;
WriteByte ( 1 ) ; // entity count
WriteInt ( a_Entity . GetUniqueID ( ) ) ;
Flush ( ) ;
}
2012-09-23 08:57:25 -07:00
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 ( ) ;
}
2012-09-02 05:09:58 -07:00
void cProtocol132 : : SendLogin ( const cPlayer & a_Player , const cWorld & a_World )
2012-08-31 14:59:57 -07:00
{
cCSLock Lock ( m_CSPacket ) ;
WriteByte ( PACKET_LOGIN ) ;
WriteInt ( a_Player . GetUniqueID ( ) ) ; // EntityID of the player
WriteString ( " default " ) ; // Level type
WriteByte ( ( int ) a_Player . GetGameMode ( ) ) ;
2013-03-11 10:15:34 -07:00
WriteByte ( ( Byte ) ( a_World . GetDimension ( ) ) ) ;
2012-08-31 14:59:57 -07:00
WriteByte ( 2 ) ; // TODO: Difficulty
WriteByte ( 0 ) ; // Unused, used to be world height
WriteByte ( 8 ) ; // Client list width or something
Flush ( ) ;
2012-09-02 05:09:58 -07:00
SendCompass ( a_World ) ;
2012-09-23 09:54:03 -07:00
// Send the initial position (so that confirmation works, FS #245):
SendPlayerMoveLook ( ) ;
2012-08-31 14:59:57 -07:00
}
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 ( ) ;
}
2012-09-11 05:01:34 -07:00
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 ( ) ;
}
2012-10-21 00:46:28 -07:00
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 / 8 ) ;
WriteByte ( a_SrcY / 8 ) ;
WriteInt ( a_SrcZ / 8 ) ;
WriteInt ( a_Data ) ;
Flush ( ) ;
}
2012-08-31 14:59:57 -07:00
void cProtocol132 : : SendSpawnMob ( const cMonster & a_Mob )
{
cCSLock Lock ( m_CSPacket ) ;
WriteByte ( PACKET_SPAWN_MOB ) ;
WriteInt ( a_Mob . GetUniqueID ( ) ) ;
WriteByte ( a_Mob . GetMobType ( ) ) ;
WriteVectorI ( ( Vector3i ) ( a_Mob . GetPosition ( ) * 32 ) ) ;
WriteByte ( 0 ) ; // yaw
WriteByte ( 0 ) ; // pitch
WriteByte ( 0 ) ; // head yaw
WriteShort ( 0 ) ; // Velocity Z
WriteShort ( 0 ) ; // Velocity X
WriteShort ( 0 ) ; // Velocity Y
AString MetaData = GetEntityMetaData ( a_Mob ) ;
SendData ( MetaData . data ( ) , MetaData . size ( ) ) ;
Flush ( ) ;
}
void cProtocol132 : : SendUnloadChunk ( int a_ChunkX , int a_ChunkZ )
{
// Not used in 1.3.2
// Does it unload chunks on its own?
}
2012-09-21 13:50:34 -07:00
void cProtocol132 : : SendWholeInventory ( const cWindow & a_Window )
{
// 1.3.2 requires player inventory slots to be sent as SetSlot packets,
// otherwise it sometimes fails to update the window
super : : SendWholeInventory ( a_Window ) ;
const cItem * Slots = m_Client - > GetPlayer ( ) - > GetInventory ( ) . GetSlots ( ) ;
int BaseOffset = a_Window . GetNumSlots ( ) - cInventory : : c_NumSlots + cInventory : : c_MainOffset ; // the number of non-inventory slots the window has; inventory follows
char WindowID = a_Window . GetWindowID ( ) ;
for ( int i = 0 ; i < cInventory : : c_NumSlots - cInventory : : c_MainOffset ; i + + )
{
SendInventorySlot ( WindowID , BaseOffset + i , Slots [ i + cInventory : : c_MainOffset ] ) ;
} // for i - Slots[]
// Send even the item being dragged:
SendInventorySlot ( - 1 , - 1 , m_Client - > GetPlayer ( ) - > GetDraggingItem ( ) ) ;
}
2012-09-06 10:36:59 -07:00
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 ;
}
2012-08-30 14:06:13 -07:00
int cProtocol132 : : ParsePacket ( unsigned char a_PacketType )
{
switch ( a_PacketType )
{
default : return super : : ParsePacket ( a_PacketType ) ; // off-load previously known packets into cProtocol125
2012-08-31 14:59:57 -07:00
case 0xcc : return ParseLocaleViewDistance ( ) ;
2012-08-30 14:06:13 -07:00
case 0xcd : return ParseClientStatuses ( ) ;
case 0xfc : return ParseEncryptionKeyResponse ( ) ;
}
2012-08-30 02:56:59 -07:00
}
2012-08-31 14:59:57 -07:00
int cProtocol132 : : ParseBlockPlace ( void )
{
HANDLE_PACKET_READ ( ReadBEInt , int , PosX ) ;
HANDLE_PACKET_READ ( ReadByte , Byte , PosY ) ;
HANDLE_PACKET_READ ( ReadBEInt , int , PosZ ) ;
2013-01-11 20:46:01 -08:00
HANDLE_PACKET_READ ( ReadChar , char , BlockFace ) ;
2012-08-31 14:59:57 -07:00
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 ) ;
2013-01-11 20:46:01 -08:00
m_Client - > HandleRightClick ( PosX , PosY , PosZ , BlockFace , CursorX , CursorY , CursorZ , HeldItem ) ;
2012-08-31 14:59:57 -07:00
return PARSE_OK ;
}
2012-08-30 02:56:59 -07:00
int cProtocol132 : : ParseHandshake ( void )
{
HANDLE_PACKET_READ ( ReadByte , Byte , ProtocolVersion ) ;
HANDLE_PACKET_READ ( ReadBEUTF16String16 , AString , Username ) ;
HANDLE_PACKET_READ ( ReadBEUTF16String16 , AString , ServerHost ) ;
2012-08-30 14:06:13 -07:00
HANDLE_PACKET_READ ( ReadBEInt , int , ServerPort ) ;
2012-08-30 02:56:59 -07:00
m_Username = Username ;
2012-08-30 14:06:13 -07:00
2012-09-05 13:30:27 -07:00
if ( ! m_Client - > HandleHandshake ( m_Username ) )
{
return PARSE_OK ; // Player is not allowed into the server
}
2012-09-02 05:09:58 -07:00
// Send a 0xFD Encryption Key Request http://wiki.vg/Protocol#0xFD
2012-09-06 10:36:59 -07:00
CryptoPP : : StringSink sink ( m_ServerPublicKey ) ; // GCC won't allow inline instantiation in the following line, damned temporary refs
2012-08-30 14:06:13 -07:00
cRoot : : Get ( ) - > GetServer ( ) - > GetPublicKey ( ) . Save ( sink ) ;
2012-09-06 10:36:59 -07:00
SendEncryptionKeyRequest ( ) ;
2012-09-02 05:09:58 -07:00
2012-08-30 14:06:13 -07:00
return PARSE_OK ;
}
int cProtocol132 : : ParseClientStatuses ( void )
{
HANDLE_PACKET_READ ( ReadByte , byte , Status ) ;
2012-10-06 10:19:56 -07:00
if ( ( Status & 1 ) = = 0 )
{
m_Client - > HandleLogin ( 39 , m_Username ) ;
}
else
{
m_Client - > HandleRespawn ( ) ;
}
2012-08-30 02:56:59 -07:00
return PARSE_OK ;
}
2012-08-30 14:06:13 -07:00
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 ;
}
2012-08-31 14:59:57 -07:00
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 ;
}
2012-08-30 14:06:13 -07:00
void cProtocol132 : : SendData ( const char * a_Data , int a_Size )
{
2012-08-31 14:59:57 -07:00
m_DataToSend . append ( a_Data , a_Size ) ;
}
void cProtocol132 : : Flush ( void )
{
2012-09-02 05:09:58 -07:00
ASSERT ( m_CSPacket . IsLockedByCurrentThread ( ) ) ; // Did all packets lock the CS properly?
2012-08-31 14:59:57 -07:00
if ( m_DataToSend . empty ( ) )
{
LOGD ( " Flushing empty " ) ;
return ;
}
const char * a_Data = m_DataToSend . data ( ) ;
int a_Size = m_DataToSend . size ( ) ;
2012-08-30 14:06:13 -07:00
if ( m_IsEncrypted )
{
2012-09-02 05:09:58 -07:00
byte Encrypted [ 8192 ] ; // Larger buffer, we may be sending lots of data (chunks)
2012-08-30 14:06:13 -07:00
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 ) ;
}
2012-08-31 14:59:57 -07:00
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 ( ) )
{
2012-09-20 13:10:46 -07:00
WriteShort ( - 1 ) ;
2012-08-31 14:59:57 -07:00
return ;
}
2012-09-20 13:10:46 -07:00
WriteShort ( ItemType ) ;
2012-08-31 14:59:57 -07:00
WriteByte ( a_Item . m_ItemCount ) ;
WriteShort ( a_Item . m_ItemDamage ) ;
// TODO: Implement enchantments
WriteShort ( - 1 ) ;
2012-08-30 14:06:13 -07:00
}
2012-09-02 05:09:58 -07:00
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 ;
2012-10-18 10:47:42 -07:00
a_Item . m_ItemDamage = ItemDamage ;
2012-09-02 05:09:58 -07:00
if ( ItemCount < = 0 )
{
a_Item . Empty ( ) ;
}
HANDLE_PACKET_READ ( ReadBEShort , short , EnchantNumBytes ) ;
if ( EnchantNumBytes < = 0 )
{
return PARSE_OK ;
}
// TODO: Enchantment not implemented yet!
if ( ! m_ReceivedData . SkipRead ( EnchantNumBytes ) )
{
return PARSE_INCOMPLETE ;
}
return PARSE_OK ;
}
void cProtocol132 : : SendCompass ( const cWorld & a_World )
{
cCSLock Lock ( m_CSPacket ) ;
WriteByte ( PACKET_COMPASS ) ;
WriteInt ( ( int ) ( a_World . GetSpawnX ( ) ) ) ;
WriteInt ( ( int ) ( a_World . GetSpawnY ( ) ) ) ;
WriteInt ( ( int ) ( a_World . GetSpawnZ ( ) ) ) ;
Flush ( ) ;
}
2012-09-06 10:36:59 -07:00
void cProtocol132 : : SendEncryptionKeyRequest ( void )
2012-09-02 05:09:58 -07:00
{
cCSLock Lock ( m_CSPacket ) ;
WriteByte ( ( char ) 0xfd ) ;
2012-09-06 09:22:08 -07:00
WriteString ( cRoot : : Get ( ) - > GetServer ( ) - > GetServerID ( ) ) ;
2012-09-06 10:36:59 -07:00
WriteShort ( ( short ) m_ServerPublicKey . size ( ) ) ;
SendData ( m_ServerPublicKey . data ( ) , m_ServerPublicKey . size ( ) ) ;
2012-09-02 05:09:58 -07:00
WriteShort ( 4 ) ;
2012-09-04 03:17:27 -07:00
WriteInt ( ( int ) ( intptr_t ) this ) ; // Using 'this' as the cryptographic nonce, so that we don't have to generate one each time :)
2012-09-02 05:09:58 -07:00
Flush ( ) ;
}
2012-08-30 14:06:13 -07:00
void cProtocol132 : : HandleEncryptionKeyResponse ( const AString & a_EncKey , const AString & a_EncNonce )
{
// Decrypt EncNonce using privkey
RSAES < PKCS1v15 > : : Decryptor rsaDecryptor ( cRoot : : Get ( ) - > GetServer ( ) - > GetPrivateKey ( ) ) ;
2012-09-04 03:17:27 -07:00
time_t CurTime = time ( NULL ) ;
CryptoPP : : RandomPool rng ;
rng . Put ( ( const byte * ) & CurTime , sizeof ( CurTime ) ) ;
2012-08-30 14:06:13 -07:00
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 ;
}
2012-09-04 03:21:03 -07:00
if ( ntohl ( * ( ( int * ) DecryptedNonce ) ) ! = ( unsigned ) ( uintptr_t ) this )
2012-08-30 14:06:13 -07:00
{
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 ;
}
2012-09-02 05:09:58 -07:00
{
// Send encryption key response:
cCSLock Lock ( m_CSPacket ) ;
WriteByte ( ( char ) 0xfc ) ;
WriteShort ( 0 ) ;
WriteShort ( 0 ) ;
Flush ( ) ;
}
2012-08-30 14:06:13 -07:00
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 ;
2012-09-06 10:36:59 -07:00
// 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 ) ;
2012-08-30 14:06:13 -07:00
}