2013-10-30 23:24:46 +01:00
// Protocol17x.cpp
/*
Implements 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)
*/
#include "Globals.h"
2014-02-15 23:16:44 +01:00
#include "json/json.h"
2013-10-30 23:24:46 +01:00
#include "Protocol17x.h"
2013-11-04 21:20:36 +01:00
#include "ChunkDataSerializer.h"
2013-10-30 23:24:46 +01:00
#include "../ClientHandle.h"
#include "../Root.h"
#include "../Server.h"
2013-11-01 16:20:15 +01:00
#include "../World.h"
2013-11-03 11:58:49 +01:00
#include "../WorldStorage/FastNBT.h"
2014-01-19 07:38:59 -08:00
#include "../WorldStorage/EnchantmentSerializer.h"
2013-11-03 11:58:49 +01:00
#include "../StringCompression.h"
2013-11-25 20:04:01 +01:00
#include "../Entities/ExpOrb.h"
2013-11-10 20:48:12 +00:00
#include "../Entities/Minecart.h"
2013-11-04 21:20:36 +01:00
#include "../Entities/FallingBlock.h"
#include "../Entities/Pickup.h"
#include "../Entities/Player.h"
2013-11-08 20:56:19 +01:00
#include "../Mobs/IncludeAllMonsters.h"
2013-11-06 12:16:44 +01:00
#include "../UI/Window.h"
2014-01-19 13:25:35 +00:00
#include "../BlockEntities/CommandBlockEntity.h"
2014-02-15 23:16:44 +01:00
#include "../CompositeChat.h"
2013-10-31 23:48:43 +01:00
2013-12-13 17:53:00 +01:00
#define HANDLE_READ(ByteBuf, Proc, Type, Var) \
2013-10-31 23:48:43 +01:00
Type Var; \
2013-12-13 17:53:00 +01:00
ByteBuf.Proc(Var);
2013-10-30 23:24:46 +01:00
2013-12-13 17:53:00 +01:00
#define HANDLE_PACKET_READ(ByteBuf, Proc, Type, Var) \
2013-11-06 20:48:35 +01:00
Type Var; \
{ \
2013-12-13 17:53:00 +01:00
if (!ByteBuf.Proc(Var)) \
2013-11-06 20:48:35 +01:00
{ \
2013-12-13 17:53:00 +01:00
ByteBuf.CheckValid(); \
2013-11-06 20:48:35 +01:00
return false; \
} \
2013-12-13 17:53:00 +01:00
ByteBuf.CheckValid(); \
2013-11-06 20:48:35 +01:00
}
2014-01-28 23:53:07 +01:00
const int MAX_ENC_LEN = 512 ; // Maximum size of the encrypted message; should be 128, but who knows...
2014-01-24 23:03:48 +01:00
// fwd: main.cpp:
2014-01-26 17:54:18 +01:00
extern bool g_ShouldLogCommIn , g_ShouldLogCommOut ;
2014-01-24 23:03:48 +01:00
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cProtocol172:
2013-10-30 23:24:46 +01:00
cProtocol172 :: cProtocol172 ( cClientHandle * a_Client , const AString & a_ServerAddress , UInt16 a_ServerPort , UInt32 a_State ) :
super ( a_Client ),
m_ServerAddress ( a_ServerAddress ),
m_ServerPort ( a_ServerPort ),
m_State ( a_State ),
m_ReceivedData ( 32 KiB ),
2013-11-03 11:58:49 +01:00
m_OutPacketBuffer ( 64 KiB ),
m_OutPacketLenBuffer ( 20 ), // 20 bytes is more than enough for one VarInt
2013-10-30 23:24:46 +01:00
m_IsEncrypted ( false )
{
2014-01-24 23:03:48 +01:00
// Create the comm log file, if so requested:
2014-01-26 17:54:18 +01:00
if ( g_ShouldLogCommIn || g_ShouldLogCommOut )
2014-01-24 23:03:48 +01:00
{
cFile :: CreateFolder ( "CommLogs" );
AString FileName = Printf ( "CommLogs/%x__%s.log" , ( unsigned ) time ( NULL ), a_Client -> GetIPString (). c_str ());
m_CommLogFile . Open ( FileName , cFile :: fmWrite );
}
2013-10-30 23:24:46 +01:00
}
void cProtocol172 :: DataReceived ( const char * a_Data , int a_Size )
{
if ( m_IsEncrypted )
{
2014-01-23 23:35:23 +01:00
Byte Decrypted [ 512 ];
2013-10-30 23:24:46 +01:00
while ( a_Size > 0 )
{
2014-02-05 13:54:47 +01:00
size_t NumBytes = ( a_Size > sizeof ( Decrypted )) ? sizeof ( Decrypted ) : a_Size ;
2014-01-23 23:35:23 +01:00
m_Decryptor . ProcessData ( Decrypted , ( Byte * ) a_Data , NumBytes );
2013-10-30 23:24:46 +01:00
AddReceivedData (( const char * ) Decrypted , NumBytes );
a_Size -= NumBytes ;
a_Data += NumBytes ;
}
}
else
{
AddReceivedData ( a_Data , a_Size );
}
}
2013-11-01 16:20:15 +01:00
void cProtocol172 :: SendAttachEntity ( const cEntity & a_Entity , const cEntity * a_Vehicle )
{
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x1b ); // Attach Entity packet
Pkt . WriteInt ( a_Entity . GetUniqueID ());
Pkt . WriteInt (( a_Vehicle != NULL ) ? a_Vehicle -> GetUniqueID () : 0 );
Pkt . WriteBool ( false );
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendBlockAction ( int a_BlockX , int a_BlockY , int a_BlockZ , char a_Byte1 , char a_Byte2 , BLOCKTYPE a_BlockType )
{
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x24 ); // Block Action packet
Pkt . WriteInt ( a_BlockX );
Pkt . WriteShort ( a_BlockY );
Pkt . WriteInt ( a_BlockZ );
Pkt . WriteByte ( a_Byte1 );
Pkt . WriteByte ( a_Byte2 );
Pkt . WriteVarInt ( a_BlockType );
2013-11-01 16:20:15 +01:00
}
2013-11-03 11:58:49 +01:00
void cProtocol172 :: SendBlockBreakAnim ( int a_EntityID , int a_BlockX , int a_BlockY , int a_BlockZ , char a_Stage )
2013-11-01 16:20:15 +01:00
{
2014-01-16 22:42:47 +00:00
cPacketizer Pkt ( * this , 0x25 ); // Block Break Animation packet
2014-01-20 19:02:37 +00:00
Pkt . WriteVarInt ( a_EntityID );
2013-11-03 11:58:49 +01:00
Pkt . WriteInt ( a_BlockX );
Pkt . WriteInt ( a_BlockY );
Pkt . WriteInt ( a_BlockZ );
Pkt . WriteChar ( a_Stage );
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendBlockChange ( int a_BlockX , int a_BlockY , int a_BlockZ , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta )
{
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x23 ); // Block Change packet
Pkt . WriteInt ( a_BlockX );
Pkt . WriteByte ( a_BlockY );
Pkt . WriteInt ( a_BlockZ );
Pkt . WriteVarInt ( a_BlockType );
Pkt . WriteByte ( a_BlockMeta );
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendBlockChanges ( int a_ChunkX , int a_ChunkZ , const sSetBlockVector & a_Changes )
{
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x22 ); // Multi Block Change packet
Pkt . WriteInt ( a_ChunkX );
Pkt . WriteInt ( a_ChunkZ );
Pkt . WriteShort (( short ) a_Changes . size ());
Pkt . WriteInt ( a_Changes . size () * 4 );
2013-11-01 16:20:15 +01:00
for ( sSetBlockVector :: const_iterator itr = a_Changes . begin (), end = a_Changes . end (); itr != end ; ++ itr )
{
unsigned int Coords = itr -> y | ( itr -> z << 8 ) | ( itr -> x << 12 );
unsigned int Blocks = itr -> BlockMeta | ( itr -> BlockType << 4 );
2013-11-03 11:58:49 +01:00
Pkt . WriteInt (( Coords << 16 ) | Blocks );
2013-11-01 16:20:15 +01:00
} // for itr - a_Changes[]
}
void cProtocol172 :: SendChat ( const AString & a_Message )
{
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x02 ); // Chat Message packet
Pkt . WriteString ( Printf ( "{ \" text \" : \" %s \" }" , EscapeString ( a_Message ). c_str ()));
2013-11-01 16:20:15 +01:00
}
2014-02-15 23:16:44 +01:00
void cProtocol172 :: SendChat ( const cCompositeChat & a_Message )
{
// Compose the complete Json string to send:
Json :: Value msg ;
msg [ "text" ] = "" ; // The client crashes without this
const cCompositeChat :: cParts & Parts = a_Message . GetParts ();
for ( cCompositeChat :: cParts :: const_iterator itr = Parts . begin (), end = Parts . end (); itr != end ; ++ itr )
{
Json :: Value Part ;
switch (( * itr ) -> m_PartType )
{
case cCompositeChat :: ptText :
{
Part [ "text" ] = ( * itr ) -> m_Text ;
AddChatPartStyle ( Part , ( * itr ) -> m_Style );
break ;
}
case cCompositeChat :: ptClientTranslated :
{
const cCompositeChat :: cClientTranslatedPart & p = ( const cCompositeChat :: cClientTranslatedPart & ) ** itr ;
Part [ "translate" ] = p . m_Text ;
Json :: Value With ;
for ( AStringVector :: const_iterator itrW = p . m_Parameters . begin (), endW = p . m_Parameters . end (); itrW != endW ; ++ itr )
{
With . append ( * itrW );
}
if ( ! p . m_Parameters . empty ())
{
Part [ "with" ] = With ;
}
AddChatPartStyle ( Part , p . m_Style );
break ;
}
case cCompositeChat :: ptUrl :
{
const cCompositeChat :: cUrlPart & p = ( const cCompositeChat :: cUrlPart & ) ** itr ;
Part [ "text" ] = p . m_Text ;
Json :: Value Url ;
Url [ "action" ] = "open_url" ;
Url [ "value" ] = p . m_Url ;
Part [ "clickEvent" ] = Url ;
AddChatPartStyle ( Part , p . m_Style );
break ;
}
case cCompositeChat :: ptSuggestCommand :
case cCompositeChat :: ptRunCommand :
{
const cCompositeChat :: cCommandPart & p = ( const cCompositeChat :: cCommandPart & ) ** itr ;
Part [ "text" ] = p . m_Text ;
Json :: Value Cmd ;
Cmd [ "action" ] = ( p . m_PartType == cCompositeChat :: ptRunCommand ) ? "run_command" : "suggest_command" ;
Cmd [ "value" ] = p . m_Command ;
Part [ "clickEvent" ] = Cmd ;
AddChatPartStyle ( Part , p . m_Style );
break ;
}
}
msg [ "extra" ]. append ( Part );
} // for itr - Parts[]
// Send the message to the client:
cPacketizer Pkt ( * this , 0x02 );
Pkt . WriteString ( msg . toStyledString ());
}
2013-11-01 16:20:15 +01:00
void cProtocol172 :: SendChunkData ( int a_ChunkX , int a_ChunkZ , cChunkDataSerializer & a_Serializer )
{
2013-11-04 21:20:36 +01:00
// Serialize first, before creating the Packetizer (the packetizer locks a CS)
// This contains the flags and bitmasks, too
const AString & ChunkData = a_Serializer . Serialize ( cChunkDataSerializer :: RELEASE_1_3_2 );
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x21 ); // Chunk Data packet
Pkt . WriteInt ( a_ChunkX );
Pkt . WriteInt ( a_ChunkZ );
Pkt . WriteBuf ( ChunkData . data (), ChunkData . size ());
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendCollectPickup ( const cPickup & a_Pickup , const cPlayer & a_Player )
{
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x0d ); // Collect Item packet
Pkt . WriteInt ( a_Pickup . GetUniqueID ());
Pkt . WriteInt ( a_Player . GetUniqueID ());
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendDestroyEntity ( const cEntity & a_Entity )
{
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x13 ); // Destroy Entities packet
Pkt . WriteByte ( 1 );
Pkt . WriteInt ( a_Entity . GetUniqueID ());
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendDisconnect ( const AString & a_Reason )
{
2014-01-13 20:32:15 +01:00
switch ( m_State )
{
case 2 :
{
// During login:
cPacketizer Pkt ( * this , 0 );
Pkt . WriteString ( Printf ( "{ \" text \" : \" %s \" }" , EscapeString ( a_Reason ). c_str ()));
break ;
}
case 3 :
{
// In-game:
cPacketizer Pkt ( * this , 0x40 );
Pkt . WriteString ( Printf ( "{ \" text \" : \" %s \" }" , EscapeString ( a_Reason ). c_str ()));
break ;
}
}
2013-11-01 16:20:15 +01:00
}
2013-11-03 11:58:49 +01:00
void cProtocol172 :: SendEditSign ( int a_BlockX , int a_BlockY , int a_BlockZ )
2013-11-01 16:20:15 +01:00
{
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x36 ); // Sign Editor Open packet
Pkt . WriteInt ( a_BlockX );
Pkt . WriteInt ( a_BlockY );
Pkt . WriteInt ( a_BlockZ );
2013-11-01 16:20:15 +01:00
}
2013-12-14 18:03:00 +01:00
void cProtocol172 :: SendEntityEffect ( const cEntity & a_Entity , int a_EffectID , int a_Amplifier , short a_Duration )
{
cPacketizer Pkt ( * this , 0x1D ); // Entity Effect packet
Pkt . WriteInt ( a_Entity . GetUniqueID ());
Pkt . WriteByte ( a_EffectID );
Pkt . WriteByte ( a_Amplifier );
Pkt . WriteShort ( a_Duration );
}
2013-11-01 16:20:15 +01:00
void cProtocol172 :: SendEntityEquipment ( const cEntity & a_Entity , short a_SlotNum , const cItem & a_Item )
{
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x04 ); // Entity Equipment packet
Pkt . WriteInt ( a_Entity . GetUniqueID ());
Pkt . WriteShort ( a_SlotNum );
Pkt . WriteItem ( a_Item );
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendEntityHeadLook ( const cEntity & a_Entity )
{
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x19 ); // Entity Head Look packet
Pkt . WriteInt ( a_Entity . GetUniqueID ());
Pkt . WriteByteAngle ( a_Entity . GetHeadYaw ());
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendEntityLook ( const cEntity & a_Entity )
{
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x16 ); // Entity Look packet
Pkt . WriteInt ( a_Entity . GetUniqueID ());
Pkt . WriteByteAngle ( a_Entity . GetYaw ());
Pkt . WriteByteAngle ( a_Entity . GetPitch ());
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendEntityMetadata ( const cEntity & a_Entity )
{
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x1c ); // Entity Metadata packet
Pkt . WriteInt ( a_Entity . GetUniqueID ());
2013-11-04 21:20:36 +01:00
Pkt . WriteEntityMetadata ( a_Entity );
2013-11-05 18:37:39 +01:00
Pkt . WriteByte ( 0x7f ); // The termination byte
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendEntityProperties ( const cEntity & a_Entity )
{
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x20 ); // Entity Properties packet
Pkt . WriteInt ( a_Entity . GetUniqueID ());
2013-11-08 20:56:19 +01:00
Pkt . WriteEntityProperties ( a_Entity );
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendEntityRelMove ( const cEntity & a_Entity , char a_RelX , char a_RelY , char a_RelZ )
{
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x15 ); // Entity Relative Move packet
Pkt . WriteInt ( a_Entity . GetUniqueID ());
Pkt . WriteByte ( a_RelX );
Pkt . WriteByte ( a_RelY );
Pkt . WriteByte ( a_RelZ );
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendEntityRelMoveLook ( const cEntity & a_Entity , char a_RelX , char a_RelY , char a_RelZ )
{
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x17 ); // Entity Look And Relative Move packet
Pkt . WriteInt ( a_Entity . GetUniqueID ());
Pkt . WriteByte ( a_RelX );
Pkt . WriteByte ( a_RelY );
Pkt . WriteByte ( a_RelZ );
Pkt . WriteByteAngle ( a_Entity . GetYaw ());
Pkt . WriteByteAngle ( a_Entity . GetPitch ());
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendEntityStatus ( const cEntity & a_Entity , char a_Status )
{
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x1a ); // Entity Status packet
Pkt . WriteInt ( a_Entity . GetUniqueID ());
Pkt . WriteChar ( a_Status );
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendEntityVelocity ( const cEntity & a_Entity )
{
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x12 ); // Entity Velocity packet
Pkt . WriteInt ( a_Entity . GetUniqueID ());
// 400 = 8000 / 20 ... Conversion from our speed in m/s to 8000 m/tick
Pkt . WriteShort (( short )( a_Entity . GetSpeedX () * 400 ));
Pkt . WriteShort (( short )( a_Entity . GetSpeedY () * 400 ));
Pkt . WriteShort (( short )( a_Entity . GetSpeedZ () * 400 ));
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendExplosion ( double a_BlockX , double a_BlockY , double a_BlockZ , float a_Radius , const cVector3iArray & a_BlocksAffected , const Vector3d & a_PlayerMotion )
{
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x27 ); // Explosion packet
Pkt . WriteFloat (( float ) a_BlockX );
Pkt . WriteFloat (( float ) a_BlockY );
Pkt . WriteFloat (( float ) a_BlockZ );
Pkt . WriteFloat (( float ) a_Radius );
Pkt . WriteInt ( a_BlocksAffected . size ());
for ( cVector3iArray :: const_iterator itr = a_BlocksAffected . begin (), end = a_BlocksAffected . end (); itr != end ; ++ itr )
{
Pkt . WriteChar (( char ) itr -> x );
Pkt . WriteChar (( char ) itr -> y );
Pkt . WriteChar (( char ) itr -> z );
} // for itr - a_BlockAffected[]
Pkt . WriteFloat (( float ) a_PlayerMotion . x );
Pkt . WriteFloat (( float ) a_PlayerMotion . y );
Pkt . WriteFloat (( float ) a_PlayerMotion . z );
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendGameMode ( eGameMode a_GameMode )
{
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x2b ); // Change Game State packet
Pkt . WriteByte ( 3 ); // Reason: Change game mode
Pkt . WriteFloat (( float ) a_GameMode );
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendHealth ( void )
{
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x06 ); // Update Health packet
Pkt . WriteFloat (( float ) m_Client -> GetPlayer () -> GetHealth ());
Pkt . WriteShort ( m_Client -> GetPlayer () -> GetFoodLevel ());
Pkt . WriteFloat (( float ) m_Client -> GetPlayer () -> GetFoodSaturationLevel ());
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendInventorySlot ( char a_WindowID , short a_SlotNum , const cItem & a_Item )
{
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x2f ); // Set Slot packet
Pkt . WriteChar ( a_WindowID );
Pkt . WriteShort ( a_SlotNum );
Pkt . WriteItem ( a_Item );
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendKeepAlive ( int a_PingID )
{
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x00 ); // Keep Alive packet
Pkt . WriteInt ( a_PingID );
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendLogin ( const cPlayer & a_Player , const cWorld & a_World )
{
2013-11-03 11:58:49 +01:00
// Send the Join Game packet:
{
cPacketizer Pkt ( * this , 0x01 ); // Join Game packet
Pkt . WriteInt ( a_Player . GetUniqueID ());
2013-11-07 22:33:46 +00:00
Pkt . WriteByte (( Byte ) a_Player . GetEffectiveGameMode () | ( cRoot :: Get () -> GetServer () -> IsHardcore () ? 0x08 : 0 )); // Hardcore flag bit 4
2013-11-03 11:58:49 +01:00
Pkt . WriteChar (( char ) a_World . GetDimension ());
Pkt . WriteByte ( 2 ); // TODO: Difficulty (set to Normal)
2014-02-14 21:33:16 +01:00
Pkt . WriteByte ( std :: min ( cRoot :: Get () -> GetServer () -> GetMaxPlayers (), 60 ));
2013-11-03 11:58:49 +01:00
Pkt . WriteString ( "default" ); // Level type - wtf?
}
2013-11-01 16:20:15 +01:00
// Send the spawn position:
2013-11-03 11:58:49 +01:00
{
cPacketizer Pkt ( * this , 0x05 ); // Spawn Position packet
Pkt . WriteInt (( int ) a_World . GetSpawnX ());
Pkt . WriteInt (( int ) a_World . GetSpawnY ());
Pkt . WriteInt (( int ) a_World . GetSpawnZ ());
}
2013-11-01 16:20:15 +01:00
// Send player abilities:
SendPlayerAbilities ();
}
void cProtocol172 :: SendPickupSpawn ( const cPickup & a_Pickup )
{
2013-11-03 11:58:49 +01:00
{
cPacketizer Pkt ( * this , 0x0e ); // Spawn Object packet
2013-11-07 20:58:47 +00:00
Pkt . WriteVarInt ( a_Pickup . GetUniqueID ());
2013-11-03 11:58:49 +01:00
Pkt . WriteByte ( 2 ); // Type = Pickup
Pkt . WriteFPInt ( a_Pickup . GetPosX ());
Pkt . WriteFPInt ( a_Pickup . GetPosY ());
Pkt . WriteFPInt ( a_Pickup . GetPosZ ());
Pkt . WriteByteAngle ( a_Pickup . GetYaw ());
Pkt . WriteByteAngle ( a_Pickup . GetPitch ());
Pkt . WriteInt ( 0 ); // No object data
}
{
cPacketizer Pkt ( * this , 0x1c ); // Entity Metadata packet
Pkt . WriteInt ( a_Pickup . GetUniqueID ());
Pkt . WriteByte (( 0x05 << 5 ) | 10 ); // Slot type + index 10
Pkt . WriteItem ( a_Pickup . GetItem ());
Pkt . WriteByte ( 0x7f ); // End of metadata
}
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendPlayerAbilities ( void )
{
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x39 ); // Player Abilities packet
2013-11-01 16:20:15 +01:00
Byte Flags = 0 ;
if ( m_Client -> GetPlayer () -> IsGameModeCreative ())
{
Flags |= 0x01 ;
2013-12-19 20:53:47 +00:00
Flags |= 0x08 ; // Godmode, used for creative
2013-11-01 16:20:15 +01:00
}
2013-12-15 14:48:17 +01:00
if ( m_Client -> GetPlayer () -> IsFlying ())
{
Flags |= 0x02 ;
}
if ( m_Client -> GetPlayer () -> CanFly ())
{
Flags |= 0x04 ;
}
2013-11-03 11:58:49 +01:00
Pkt . WriteByte ( Flags );
// TODO: Pkt.WriteFloat(m_Client->GetPlayer()->GetMaxFlyingSpeed());
Pkt . WriteFloat ( 0.05f );
Pkt . WriteFloat (( float ) m_Client -> GetPlayer () -> GetMaxSpeed ());
2013-11-01 16:20:15 +01:00
}
2013-12-06 23:47:07 +00:00
void cProtocol172 :: SendEntityAnimation ( const cEntity & a_Entity , char a_Animation )
2013-11-01 16:20:15 +01:00
{
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x0b ); // Animation packet
2013-12-06 23:47:07 +00:00
Pkt . WriteVarInt ( a_Entity . GetUniqueID ());
2013-11-03 11:58:49 +01:00
Pkt . WriteChar ( a_Animation );
2013-11-01 16:20:15 +01:00
}
2013-12-22 14:45:25 +01:00
void cProtocol172 :: SendParticleEffect ( const AString & a_ParticleName , float a_SrcX , float a_SrcY , float a_SrcZ , float a_OffsetX , float a_OffsetY , float a_OffsetZ , float a_ParticleData , int a_ParticleAmmount )
{
cPacketizer Pkt ( * this , 0x2A );
Pkt . WriteString ( a_ParticleName );
Pkt . WriteFloat ( a_SrcX );
Pkt . WriteFloat ( a_SrcY );
Pkt . WriteFloat ( a_SrcZ );
Pkt . WriteFloat ( a_OffsetX );
Pkt . WriteFloat ( a_OffsetY );
Pkt . WriteFloat ( a_OffsetZ );
Pkt . WriteFloat ( a_ParticleData );
Pkt . WriteInt ( a_ParticleAmmount );
}
2013-11-01 16:20:15 +01:00
void cProtocol172 :: SendPlayerListItem ( const cPlayer & a_Player , bool a_IsOnline )
{
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x38 ); // Playerlist Item packet
Pkt . WriteString ( a_Player . GetName ());
Pkt . WriteBool ( a_IsOnline );
2013-11-10 22:55:42 +01:00
Pkt . WriteShort ( a_IsOnline ? a_Player . GetClientHandle () -> GetPing () : 0 );
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendPlayerMaxSpeed ( void )
{
2013-11-08 22:40:31 +01:00
cPacketizer Pkt ( * this , 0x20 ); // Entity Properties
Pkt . WriteInt ( m_Client -> GetPlayer () -> GetUniqueID ());
Pkt . WriteInt ( 1 ); // Count
Pkt . WriteString ( "generic.movementSpeed" );
Pkt . WriteDouble ( 0.1 );
if ( m_Client -> GetPlayer () -> IsSprinting ())
{
Pkt . WriteShort ( 1 ); // Modifier count
Pkt . WriteInt64 ( 0x662a6b8dda3e4c1c );
Pkt . WriteInt64 ( 0x881396ea6097278d ); // UUID of the modifier
Pkt . WriteDouble ( 0.3 );
Pkt . WriteByte ( 2 );
}
else
{
Pkt . WriteShort ( 0 ); // Modifier count
}
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendPlayerMoveLook ( void )
{
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x08 ); // Player Position And Look packet
Pkt . WriteDouble ( m_Client -> GetPlayer () -> GetPosX ());
2013-12-17 19:56:17 +01:00
// Protocol docs say this is PosY, but #323 says this is eye-pos
// Moreover, the "+ 0.001" is there because otherwise the player falls through the block they were standing on.
Pkt . WriteDouble ( m_Client -> GetPlayer () -> GetStance () + 0.001 );
2013-11-03 11:58:49 +01:00
Pkt . WriteDouble ( m_Client -> GetPlayer () -> GetPosZ ());
Pkt . WriteFloat (( float ) m_Client -> GetPlayer () -> GetYaw ());
Pkt . WriteFloat (( float ) m_Client -> GetPlayer () -> GetPitch ());
Pkt . WriteBool ( m_Client -> GetPlayer () -> IsOnGround ());
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendPlayerPosition ( void )
{
2013-11-03 11:58:49 +01:00
// There is no dedicated packet for this, send the whole thing:
SendPlayerMoveLook ();
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendPlayerSpawn ( const cPlayer & a_Player )
{
2013-11-03 11:58:49 +01:00
// Called to spawn another player for the client
cPacketizer Pkt ( * this , 0x0c ); // Spawn Player packet
2013-11-10 22:55:42 +01:00
Pkt . WriteVarInt ( a_Player . GetUniqueID ());
2013-11-03 11:58:49 +01:00
Pkt . WriteString ( Printf ( "%d" , a_Player . GetUniqueID ())); // TODO: Proper UUID
Pkt . WriteString ( a_Player . GetName ());
Pkt . WriteFPInt ( a_Player . GetPosX ());
Pkt . WriteFPInt ( a_Player . GetPosY ());
Pkt . WriteFPInt ( a_Player . GetPosZ ());
Pkt . WriteByteAngle ( a_Player . GetYaw ());
Pkt . WriteByteAngle ( a_Player . GetPitch ());
short ItemType = a_Player . GetEquippedItem (). IsEmpty () ? 0 : a_Player . GetEquippedItem (). m_ItemType ;
Pkt . WriteShort ( ItemType );
Pkt . WriteByte (( 3 << 5 ) | 6 ); // Metadata: float + index 6
Pkt . WriteFloat (( float ) a_Player . GetHealth ());
Pkt . WriteByte ( 0x7f ); // Metadata: end
2013-11-01 16:20:15 +01:00
}
2014-01-09 11:39:42 +01:00
void cProtocol172 :: SendPluginMessage ( const AString & a_Channel , const AString & a_Message )
{
2014-01-09 14:24:57 +01:00
cPacketizer Pkt ( * this , 0x3f );
2014-01-09 11:39:42 +01:00
Pkt . WriteString ( a_Channel );
Pkt . WriteShort (( short ) a_Message . size ());
Pkt . WriteBuf ( a_Message . data (), a_Message . size ());
}
2013-12-14 18:03:00 +01:00
void cProtocol172 :: SendRemoveEntityEffect ( const cEntity & a_Entity , int a_EffectID )
{
cPacketizer Pkt ( * this , 0x1E );
Pkt . WriteInt ( a_Entity . GetUniqueID ());
Pkt . WriteByte ( a_EffectID );
}
2013-11-01 16:20:15 +01:00
void cProtocol172 :: SendRespawn ( void )
{
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x07 ); // Respawn packet
Pkt . WriteInt ( m_Client -> GetPlayer () -> GetWorld () -> GetDimension ());
Pkt . WriteByte ( 2 ); // TODO: Difficulty (set to Normal)
Pkt . WriteByte (( Byte ) m_Client -> GetPlayer () -> GetEffectiveGameMode ());
Pkt . WriteString ( "default" );
2013-11-01 16:20:15 +01:00
}
2013-11-16 02:23:50 +11:00
void cProtocol172 :: SendExperience ( void )
2013-11-15 08:35:02 +11:00
{
cPacketizer Pkt ( * this , 0x1F ); //Experience Packet
2013-11-16 21:38:57 +11:00
Pkt . WriteFloat ( m_Client -> GetPlayer () -> GetXpPercentage ());
Pkt . WriteShort ( m_Client -> GetPlayer () -> GetXpLevel ());
Pkt . WriteShort ( m_Client -> GetPlayer () -> GetCurrentXp ());
2013-11-15 08:35:02 +11:00
}
2013-11-25 21:43:43 +01:00
void cProtocol172 :: SendExperienceOrb ( const cExpOrb & a_ExpOrb )
2013-11-25 20:04:01 +01:00
{
cPacketizer Pkt ( * this , 0x11 );
2013-11-25 21:43:43 +01:00
Pkt . WriteVarInt ( a_ExpOrb . GetUniqueID ());
Pkt . WriteInt (( int ) a_ExpOrb . GetPosX ());
Pkt . WriteInt (( int ) a_ExpOrb . GetPosY ());
Pkt . WriteInt (( int ) a_ExpOrb . GetPosZ ());
Pkt . WriteShort ( a_ExpOrb . GetReward ());
2013-11-25 20:04:01 +01:00
}
2014-01-21 15:58:17 +02:00
void cProtocol172 :: SendScoreboardObjective ( const AString & a_Name , const AString & a_DisplayName , Byte a_Mode )
{
2014-01-21 19:43:13 +02:00
cPacketizer Pkt ( * this , 0x3B );
2014-01-21 15:58:17 +02:00
Pkt . WriteString ( a_Name );
Pkt . WriteString ( a_DisplayName );
Pkt . WriteByte ( a_Mode );
}
void cProtocol172 :: SendScoreUpdate ( const AString & a_Objective , const AString & a_Player , cObjective :: Score a_Score , Byte a_Mode )
{
2014-01-21 19:43:13 +02:00
cPacketizer Pkt ( * this , 0x3C );
2014-01-21 15:58:17 +02:00
Pkt . WriteString ( a_Player );
Pkt . WriteByte ( a_Mode );
if ( a_Mode != 1 )
{
Pkt . WriteString ( a_Objective );
Pkt . WriteInt (( int ) a_Score );
}
}
void cProtocol172 :: SendDisplayObjective ( const AString & a_Objective , cScoreboard :: eDisplaySlot a_Display )
{
2014-01-21 19:43:13 +02:00
cPacketizer Pkt ( * this , 0x3D );
2014-01-21 15:58:17 +02:00
Pkt . WriteByte (( int ) a_Display );
Pkt . WriteString ( a_Objective );
}
2013-11-01 16:20:15 +01:00
void cProtocol172 :: SendSoundEffect ( const AString & a_SoundName , int a_SrcX , int a_SrcY , int a_SrcZ , float a_Volume , float a_Pitch ) // a_Src coords are Block * 8
{
2013-11-04 21:20:36 +01:00
cPacketizer Pkt ( * this , 0x29 ); // Sound Effect packet
Pkt . WriteString ( a_SoundName );
Pkt . WriteInt ( a_SrcX );
Pkt . WriteInt ( a_SrcY );
Pkt . WriteInt ( a_SrcZ );
Pkt . WriteFloat ( a_Volume );
Pkt . WriteByte (( Byte )( a_Pitch * 63 ));
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendSoundParticleEffect ( int a_EffectID , int a_SrcX , int a_SrcY , int a_SrcZ , int a_Data )
{
2013-11-04 21:20:36 +01:00
cPacketizer Pkt ( * this , 0x28 ); // Effect packet
Pkt . WriteInt ( a_EffectID );
2013-11-10 22:20:25 +00:00
Pkt . WriteInt ( a_SrcX );
Pkt . WriteByte ( a_SrcY );
2013-11-04 21:20:36 +01:00
Pkt . WriteInt ( a_SrcZ );
Pkt . WriteInt ( a_Data );
Pkt . WriteBool ( false );
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendSpawnFallingBlock ( const cFallingBlock & a_FallingBlock )
{
2013-11-04 21:20:36 +01:00
cPacketizer Pkt ( * this , 0x0e ); // Spawn Object packet
2013-11-07 20:58:47 +00:00
Pkt . WriteVarInt ( a_FallingBlock . GetUniqueID ());
2013-11-04 21:20:36 +01:00
Pkt . WriteByte ( 70 ); // Falling block
Pkt . WriteFPInt ( a_FallingBlock . GetPosX ());
Pkt . WriteFPInt ( a_FallingBlock . GetPosY ());
Pkt . WriteFPInt ( a_FallingBlock . GetPosZ ());
Pkt . WriteByteAngle ( a_FallingBlock . GetYaw ());
Pkt . WriteByteAngle ( a_FallingBlock . GetPitch ());
2013-12-08 00:32:02 +00:00
Pkt . WriteInt ((( int ) a_FallingBlock . GetBlockType ()) | ((( int ) a_FallingBlock . GetBlockMeta ()) << 16 )); // Or 0x10
2013-11-04 21:20:36 +01:00
Pkt . WriteShort (( short )( a_FallingBlock . GetSpeedX () * 400 ));
Pkt . WriteShort (( short )( a_FallingBlock . GetSpeedY () * 400 ));
Pkt . WriteShort (( short )( a_FallingBlock . GetSpeedZ () * 400 ));
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendSpawnMob ( const cMonster & a_Mob )
{
2013-11-04 21:20:36 +01:00
cPacketizer Pkt ( * this , 0x0f ); // Spawn Mob packet
2013-11-07 17:11:36 +01:00
Pkt . WriteVarInt ( a_Mob . GetUniqueID ());
2013-11-04 21:20:36 +01:00
Pkt . WriteByte (( Byte ) a_Mob . GetMobType ());
Pkt . WriteFPInt ( a_Mob . GetPosX ());
Pkt . WriteFPInt ( a_Mob . GetPosY ());
Pkt . WriteFPInt ( a_Mob . GetPosZ ());
Pkt . WriteByteAngle ( a_Mob . GetPitch ());
Pkt . WriteByteAngle ( a_Mob . GetHeadYaw ());
Pkt . WriteByteAngle ( a_Mob . GetYaw ());
Pkt . WriteShort (( short )( a_Mob . GetSpeedX () * 400 ));
Pkt . WriteShort (( short )( a_Mob . GetSpeedY () * 400 ));
Pkt . WriteShort (( short )( a_Mob . GetSpeedZ () * 400 ));
Pkt . WriteEntityMetadata ( a_Mob );
Pkt . WriteByte ( 0x7f ); // Metadata terminator
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendSpawnObject ( const cEntity & a_Entity , char a_ObjectType , int a_ObjectData , Byte a_Yaw , Byte a_Pitch )
{
2013-11-04 21:20:36 +01:00
cPacketizer Pkt ( * this , 0xe ); // Spawn Object packet
2013-11-07 18:49:48 +00:00
Pkt . WriteVarInt ( a_Entity . GetUniqueID ());
2013-11-04 21:20:36 +01:00
Pkt . WriteByte ( a_ObjectType );
Pkt . WriteFPInt ( a_Entity . GetPosX ());
Pkt . WriteFPInt ( a_Entity . GetPosY ());
Pkt . WriteFPInt ( a_Entity . GetPosZ ());
Pkt . WriteByteAngle ( a_Entity . GetYaw ());
Pkt . WriteByteAngle ( a_Entity . GetPitch ());
Pkt . WriteInt ( a_ObjectData );
if ( a_ObjectData != 0 )
{
Pkt . WriteShort (( short )( a_Entity . GetSpeedX () * 400 ));
Pkt . WriteShort (( short )( a_Entity . GetSpeedY () * 400 ));
Pkt . WriteShort (( short )( a_Entity . GetSpeedZ () * 400 ));
}
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendSpawnVehicle ( const cEntity & a_Vehicle , char a_VehicleType , char a_VehicleSubType )
{
2013-11-04 21:20:36 +01:00
cPacketizer Pkt ( * this , 0xe ); // Spawn Object packet
2013-11-07 20:58:47 +00:00
Pkt . WriteVarInt ( a_Vehicle . GetUniqueID ());
2013-11-04 21:20:36 +01:00
Pkt . WriteByte ( a_VehicleType );
Pkt . WriteFPInt ( a_Vehicle . GetPosX ());
Pkt . WriteFPInt ( a_Vehicle . GetPosY ());
Pkt . WriteFPInt ( a_Vehicle . GetPosZ ());
Pkt . WriteByteAngle ( a_Vehicle . GetYaw ());
Pkt . WriteByteAngle ( a_Vehicle . GetPitch ());
Pkt . WriteInt ( a_VehicleSubType );
if ( a_VehicleSubType != 0 )
{
Pkt . WriteShort (( short )( a_Vehicle . GetSpeedX () * 400 ));
Pkt . WriteShort (( short )( a_Vehicle . GetSpeedY () * 400 ));
Pkt . WriteShort (( short )( a_Vehicle . GetSpeedZ () * 400 ));
}
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendTabCompletionResults ( const AStringVector & a_Results )
{
2013-12-08 13:37:10 +00:00
cPacketizer Pkt ( * this , 0x3a ); // Tab-Complete packet
Pkt . WriteVarInt ( a_Results . size ());
2013-11-04 21:20:36 +01:00
for ( AStringVector :: const_iterator itr = a_Results . begin (), end = a_Results . end (); itr != end ; ++ itr )
{
2013-12-08 13:37:10 +00:00
Pkt . WriteString ( * itr );
2013-11-04 21:20:36 +01:00
}
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendTeleportEntity ( const cEntity & a_Entity )
{
2013-11-04 21:20:36 +01:00
cPacketizer Pkt ( * this , 0x18 );
Pkt . WriteInt ( a_Entity . GetUniqueID ());
Pkt . WriteFPInt ( a_Entity . GetPosX ());
Pkt . WriteFPInt ( a_Entity . GetPosY ());
Pkt . WriteFPInt ( a_Entity . GetPosZ ());
Pkt . WriteByteAngle ( a_Entity . GetYaw ());
Pkt . WriteByteAngle ( a_Entity . GetPitch ());
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendThunderbolt ( int a_BlockX , int a_BlockY , int a_BlockZ )
{
2013-11-04 21:20:36 +01:00
cPacketizer Pkt ( * this , 0x2c ); // Spawn Global Entity packet
Pkt . WriteVarInt ( 0 ); // EntityID = 0, always
Pkt . WriteByte ( 1 ); // Type = Thunderbolt
Pkt . WriteFPInt ( a_BlockX );
Pkt . WriteFPInt ( a_BlockY );
Pkt . WriteFPInt ( a_BlockZ );
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendTimeUpdate ( Int64 a_WorldAge , Int64 a_TimeOfDay )
{
2013-11-04 21:20:36 +01:00
cPacketizer Pkt ( * this , 0x03 );
Pkt . WriteInt64 ( a_WorldAge );
Pkt . WriteInt64 ( a_TimeOfDay );
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendUnloadChunk ( int a_ChunkX , int a_ChunkZ )
{
2013-11-05 18:37:39 +01:00
cPacketizer Pkt ( * this , 0x21 ); // Chunk Data packet
Pkt . WriteInt ( a_ChunkX );
Pkt . WriteInt ( a_ChunkZ );
Pkt . WriteBool ( true );
Pkt . WriteShort ( 0 ); // Primary bitmap
Pkt . WriteShort ( 0 ); // Add bitmap
Pkt . WriteInt ( 0 ); // Compressed data size
2013-11-01 16:20:15 +01:00
}
2014-01-19 19:42:25 +00:00
void cProtocol172 :: SendUpdateBlockEntity ( cBlockEntity & a_BlockEntity )
2014-01-19 00:54:38 +00:00
{
cPacketizer Pkt ( * this , 0x35 ); // Update tile entity packet
2014-01-19 19:42:25 +00:00
Pkt . WriteInt ( a_BlockEntity . GetPosX ());
Pkt . WriteShort ( a_BlockEntity . GetPosY ());
Pkt . WriteInt ( a_BlockEntity . GetPosZ ());
Byte Action = 0 ;
switch ( a_BlockEntity . GetBlockType ())
{
case E_BLOCK_MOB_SPAWNER : Action = 1 ; break ; // Update mob spawner spinny mob thing
case E_BLOCK_COMMAND_BLOCK : Action = 2 ; break ; // Update command block text
default : ASSERT ( ! "Unhandled or unimplemented BlockEntity update request!" ); break ;
}
Pkt . WriteByte ( Action );
2014-01-19 00:54:38 +00:00
2014-01-19 13:25:35 +00:00
Pkt . WriteBlockEntity ( a_BlockEntity );
2014-01-19 00:54:38 +00:00
}
2013-11-01 16:20:15 +01:00
void cProtocol172 :: 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 )
{
2013-11-05 18:37:39 +01:00
cPacketizer Pkt ( * this , 0x33 );
Pkt . WriteInt ( a_BlockX );
Pkt . WriteShort (( short ) a_BlockY );
Pkt . WriteInt ( a_BlockZ );
2014-01-29 17:59:49 +01:00
// Need to send only up to 15 chars, otherwise the client crashes (#598)
Pkt . WriteString ( a_Line1 . substr ( 0 , 15 ));
Pkt . WriteString ( a_Line2 . substr ( 0 , 15 ));
Pkt . WriteString ( a_Line3 . substr ( 0 , 15 ));
Pkt . WriteString ( a_Line4 . substr ( 0 , 15 ));
2013-11-01 16:20:15 +01:00
}
2013-11-05 18:37:39 +01:00
void cProtocol172 :: SendUseBed ( const cEntity & a_Entity , int a_BlockX , int a_BlockY , int a_BlockZ )
2013-11-01 16:20:15 +01:00
{
2013-11-05 18:37:39 +01:00
cPacketizer Pkt ( * this , 0x0a );
Pkt . WriteInt ( a_Entity . GetUniqueID ());
Pkt . WriteInt ( a_BlockX );
Pkt . WriteByte (( Byte ) a_BlockY );
Pkt . WriteInt ( a_BlockZ );
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendWeather ( eWeather a_Weather )
{
2013-11-12 21:43:20 +00:00
{
cPacketizer Pkt ( * this , 0x2b ); // Change Game State packet
Pkt . WriteByte (( a_Weather == wSunny ) ? 1 : 2 ); // End rain / begin rain
Pkt . WriteFloat ( 0 ); // Unused for weather
}
// TODO: Fade effect, somehow
2013-11-01 16:20:15 +01:00
}
2013-11-06 12:16:44 +01:00
void cProtocol172 :: SendWholeInventory ( const cWindow & a_Window )
2013-11-01 16:20:15 +01:00
{
2013-11-06 12:16:44 +01:00
cPacketizer Pkt ( * this , 0x30 ); // Window Items packet
Pkt . WriteChar ( a_Window . GetWindowID ());
Pkt . WriteShort ( a_Window . GetNumSlots ());
cItems Slots ;
a_Window . GetSlots ( * ( m_Client -> GetPlayer ()), Slots );
for ( cItems :: const_iterator itr = Slots . begin (), end = Slots . end (); itr != end ; ++ itr )
{
Pkt . WriteItem ( * itr );
} // for itr - Slots[]
2013-11-01 16:20:15 +01:00
}
2013-11-06 12:16:44 +01:00
void cProtocol172 :: SendWindowClose ( const cWindow & a_Window )
2013-11-01 16:20:15 +01:00
{
2013-11-06 12:16:44 +01:00
cPacketizer Pkt ( * this , 0x2e );
Pkt . WriteChar ( a_Window . GetWindowID ());
2013-11-01 16:20:15 +01:00
}
2013-11-08 21:32:14 +01:00
void cProtocol172 :: SendWindowOpen ( const cWindow & a_Window )
2013-11-01 16:20:15 +01:00
{
2013-11-08 21:32:14 +01:00
if ( a_Window . GetWindowType () < 0 )
{
// Do not send this packet for player inventory windows
return ;
}
2013-11-06 12:16:44 +01:00
cPacketizer Pkt ( * this , 0x2d );
2013-11-08 21:32:14 +01:00
Pkt . WriteChar ( a_Window . GetWindowID ());
Pkt . WriteChar ( a_Window . GetWindowType ());
Pkt . WriteString ( a_Window . GetWindowTitle ());
Pkt . WriteChar ( a_Window . GetNumNonInventorySlots ());
2013-11-06 12:16:44 +01:00
Pkt . WriteBool ( true );
2013-11-08 21:32:14 +01:00
if ( a_Window . GetWindowType () == cWindow :: wtAnimalChest )
2013-11-06 12:16:44 +01:00
{
2013-11-08 21:32:14 +01:00
Pkt . WriteInt ( 0 ); // TODO: The animal's EntityID
2013-11-06 12:16:44 +01:00
}
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendWindowProperty ( const cWindow & a_Window , short a_Property , short a_Value )
{
2013-11-06 12:16:44 +01:00
cPacketizer Pkt ( * this , 0x31 ); // Window Property packet
Pkt . WriteChar ( a_Window . GetWindowID ());
Pkt . WriteShort ( a_Property );
Pkt . WriteShort ( a_Value );
2013-11-01 16:20:15 +01:00
}
2013-10-30 23:24:46 +01:00
void cProtocol172 :: AddReceivedData ( const char * a_Data , int a_Size )
{
2014-01-24 23:03:48 +01:00
// Write the incoming data into the comm log file:
2014-01-26 17:54:18 +01:00
if ( g_ShouldLogCommIn )
2014-01-24 23:03:48 +01:00
{
2014-01-25 15:06:21 +01:00
if ( m_ReceivedData . GetReadableSpace () > 0 )
{
AString AllData ;
int OldReadableSpace = m_ReceivedData . GetReadableSpace ();
m_ReceivedData . ReadAll ( AllData );
m_ReceivedData . ResetRead ();
m_ReceivedData . SkipRead ( m_ReceivedData . GetReadableSpace () - OldReadableSpace );
2014-01-26 17:54:18 +01:00
ASSERT ( m_ReceivedData . GetReadableSpace () == OldReadableSpace );
2014-01-25 15:06:21 +01:00
AString Hex ;
CreateHexDump ( Hex , AllData . data (), AllData . size (), 16 );
2014-01-26 17:54:18 +01:00
m_CommLogFile . Printf ( "Incoming data, %d (0x%x) unparsed bytes already present in buffer: \n %s \n " ,
2014-01-25 15:06:21 +01:00
AllData . size (), AllData . size (), Hex . c_str ()
);
}
2014-01-24 23:03:48 +01:00
AString Hex ;
CreateHexDump ( Hex , a_Data , a_Size , 16 );
2014-01-25 15:06:21 +01:00
m_CommLogFile . Printf ( "Incoming data: %d (0x%x) bytes: \n %s \n " ,
a_Size , a_Size , Hex . c_str ()
2014-01-24 23:03:48 +01:00
);
2014-01-26 17:54:18 +01:00
m_CommLogFile . Flush ();
2014-01-24 23:03:48 +01:00
}
2013-10-30 23:24:46 +01:00
if ( ! m_ReceivedData . Write ( a_Data , a_Size ))
{
// Too much data in the incoming queue, report to caller:
m_Client -> PacketBufferFull ();
return ;
}
// Handle all complete packets:
2014-01-07 14:24:25 +01:00
for (;;)
2013-10-30 23:24:46 +01:00
{
UInt32 PacketLen ;
if ( ! m_ReceivedData . ReadVarInt ( PacketLen ))
{
// Not enough data
2014-01-26 17:54:18 +01:00
m_ReceivedData . ResetRead ();
break ;
2013-10-30 23:24:46 +01:00
}
if ( ! m_ReceivedData . CanReadBytes ( PacketLen ))
{
// The full packet hasn't been received yet
2014-01-26 17:54:18 +01:00
m_ReceivedData . ResetRead ();
break ;
2013-10-30 23:24:46 +01:00
}
2013-12-13 17:53:00 +01:00
cByteBuffer bb ( PacketLen + 1 );
VERIFY ( m_ReceivedData . ReadToByteBuffer ( bb , ( int ) PacketLen ));
m_ReceivedData . CommitRead ();
// Write one NUL extra, so that we can detect over-reads
bb . Write ( " \0 " , 1 );
2013-10-30 23:24:46 +01:00
UInt32 PacketType ;
2013-12-13 17:53:00 +01:00
if ( ! bb . ReadVarInt ( PacketType ))
2013-10-30 23:24:46 +01:00
{
// Not enough data
2014-01-26 17:54:18 +01:00
break ;
2013-10-30 23:24:46 +01:00
}
2013-11-03 11:58:49 +01:00
2014-01-24 23:03:48 +01:00
// Log the packet info into the comm log file:
2014-01-26 17:54:18 +01:00
if ( g_ShouldLogCommIn )
2014-01-24 23:03:48 +01:00
{
AString PacketData ;
bb . ReadAll ( PacketData );
bb . ResetRead ();
bb . ReadVarInt ( PacketType );
ASSERT ( PacketData . size () > 0 );
PacketData . resize ( PacketData . size () - 1 );
AString PacketDataHex ;
CreateHexDump ( PacketDataHex , PacketData . data (), PacketData . size (), 16 );
m_CommLogFile . Printf ( "Next incoming packet is type %u (0x%x), length %u (0x%x) at state %d. Payload: \n %s \n " ,
PacketType , PacketType , PacketLen , PacketLen , m_State , PacketDataHex . c_str ()
);
}
2014-01-16 15:26:58 +01:00
if ( ! HandlePacket ( bb , PacketType ))
{
2014-01-16 20:39:59 +01:00
// Unknown packet, already been reported, but without the length. Log the length here:
2014-01-17 10:12:04 +01:00
LOGWARNING ( "Unhandled packet: type 0x%x, state %d, length %u" , PacketType , m_State , PacketLen );
2014-01-16 20:39:59 +01:00
#ifdef _DEBUG
// Dump the packet contents into the log:
bb . ResetRead ();
AString Packet ;
bb . ReadAll ( Packet );
Packet . resize ( Packet . size () - 1 ); // Drop the final NUL pushed there for over-read detection
AString Out ;
CreateHexDump ( Out , Packet . data (), ( int ) Packet . size (), 24 );
LOGD ( "Packet contents: \n %s" , Out . c_str ());
#endif // _DEBUG
2014-01-25 15:28:16 +01:00
// Put a message in the comm log:
2014-01-26 17:54:18 +01:00
if ( g_ShouldLogCommIn )
2014-01-25 15:28:16 +01:00
{
m_CommLogFile . Printf ( "^^^^^^ Unhandled packet ^^^^^^ \n\n\n " );
}
2014-01-16 15:26:58 +01:00
return ;
}
2013-10-31 23:48:43 +01:00
2013-12-13 17:53:00 +01:00
if ( bb . GetReadableSpace () != 1 )
2013-10-31 23:48:43 +01:00
{
2013-12-13 17:53:00 +01:00
// Read more or less than packet length, report as error
2014-01-17 10:12:04 +01:00
LOGWARNING ( "Protocol 1.7: Wrong number of bytes read for packet 0x%x, state %d. Read %u bytes, packet contained %u bytes" ,
PacketType , m_State , bb . GetUsedSpace () - bb . GetReadableSpace (), PacketLen
2014-01-16 15:26:58 +01:00
);
2014-01-25 15:28:16 +01:00
// Put a message in the comm log:
2014-01-26 17:54:18 +01:00
if ( g_ShouldLogCommIn )
2014-01-25 15:28:16 +01:00
{
m_CommLogFile . Printf ( "^^^^^^ Wrong number of bytes read for this packet (exp %d left, got %d left) ^^^^^^ \n\n\n " ,
1 , bb . GetReadableSpace ()
);
m_CommLogFile . Flush ();
}
2013-12-13 17:53:00 +01:00
ASSERT ( ! "Read wrong number of bytes!" );
2013-10-31 23:48:43 +01:00
m_Client -> PacketError ( PacketType );
}
2014-01-26 17:54:18 +01:00
} // for(ever)
// Log any leftover bytes into the logfile:
if ( g_ShouldLogCommIn && ( m_ReceivedData . GetReadableSpace () > 0 ))
{
AString AllData ;
int OldReadableSpace = m_ReceivedData . GetReadableSpace ();
m_ReceivedData . ReadAll ( AllData );
m_ReceivedData . ResetRead ();
m_ReceivedData . SkipRead ( m_ReceivedData . GetReadableSpace () - OldReadableSpace );
ASSERT ( m_ReceivedData . GetReadableSpace () == OldReadableSpace );
AString Hex ;
CreateHexDump ( Hex , AllData . data (), AllData . size (), 16 );
m_CommLogFile . Printf ( "There are %d (0x%x) bytes of non-parse-able data left in the buffer: \n %s" ,
m_ReceivedData . GetReadableSpace (), m_ReceivedData . GetReadableSpace (), Hex . c_str ()
);
m_CommLogFile . Flush ();
}
2013-10-30 23:24:46 +01:00
}
2014-01-16 15:26:58 +01:00
bool cProtocol172 :: HandlePacket ( cByteBuffer & a_ByteBuffer , UInt32 a_PacketType )
2013-10-30 23:24:46 +01:00
{
switch ( m_State )
{
case 1 :
{
// Status
switch ( a_PacketType )
{
2014-01-16 15:26:58 +01:00
case 0x00 : HandlePacketStatusRequest ( a_ByteBuffer ); return true ;
case 0x01 : HandlePacketStatusPing ( a_ByteBuffer ); return true ;
2013-10-30 23:24:46 +01:00
}
break ;
}
case 2 :
{
// Login
2013-10-31 23:48:43 +01:00
switch ( a_PacketType )
{
2014-01-16 15:26:58 +01:00
case 0x00 : HandlePacketLoginStart ( a_ByteBuffer ); return true ;
case 0x01 : HandlePacketLoginEncryptionResponse ( a_ByteBuffer ); return true ;
2013-10-31 23:48:43 +01:00
}
2013-10-30 23:24:46 +01:00
break ;
}
case 3 :
{
// Game
2013-10-31 23:48:43 +01:00
switch ( a_PacketType )
{
2014-01-16 15:26:58 +01:00
case 0x00 : HandlePacketKeepAlive ( a_ByteBuffer ); return true ;
case 0x01 : HandlePacketChatMessage ( a_ByteBuffer ); return true ;
case 0x02 : HandlePacketUseEntity ( a_ByteBuffer ); return true ;
case 0x03 : HandlePacketPlayer ( a_ByteBuffer ); return true ;
case 0x04 : HandlePacketPlayerPos ( a_ByteBuffer ); return true ;
case 0x05 : HandlePacketPlayerLook ( a_ByteBuffer ); return true ;
case 0x06 : HandlePacketPlayerPosLook ( a_ByteBuffer ); return true ;
case 0x07 : HandlePacketBlockDig ( a_ByteBuffer ); return true ;
case 0x08 : HandlePacketBlockPlace ( a_ByteBuffer ); return true ;
case 0x09 : HandlePacketSlotSelect ( a_ByteBuffer ); return true ;
case 0x0a : HandlePacketAnimation ( a_ByteBuffer ); return true ;
case 0x0b : HandlePacketEntityAction ( a_ByteBuffer ); return true ;
case 0x0c : HandlePacketSteerVehicle ( a_ByteBuffer ); return true ;
case 0x0d : HandlePacketWindowClose ( a_ByteBuffer ); return true ;
case 0x0e : HandlePacketWindowClick ( a_ByteBuffer ); return true ;
2013-10-31 23:48:43 +01:00
case 0x0f : // Confirm transaction - not used in MCS
2014-01-16 15:26:58 +01:00
case 0x10 : HandlePacketCreativeInventoryAction ( a_ByteBuffer ); return true ;
case 0x12 : HandlePacketUpdateSign ( a_ByteBuffer ); return true ;
case 0x13 : HandlePacketPlayerAbilities ( a_ByteBuffer ); return true ;
case 0x14 : HandlePacketTabComplete ( a_ByteBuffer ); return true ;
case 0x15 : HandlePacketClientSettings ( a_ByteBuffer ); return true ;
case 0x16 : HandlePacketClientStatus ( a_ByteBuffer ); return true ;
case 0x17 : HandlePacketPluginMessage ( a_ByteBuffer ); return true ;
2013-10-31 23:48:43 +01:00
}
2013-10-30 23:24:46 +01:00
break ;
}
2014-01-17 10:12:04 +01:00
default :
{
// Received a packet in an unknown state, report:
LOGWARNING ( "Received a packet in an unknown protocol state %d. Ignoring further packets." , m_State );
// Cannot kick the client - we don't know this state and thus the packet number for the kick packet
// Switch to a state when all further packets are silently ignored:
m_State = 255 ;
return false ;
}
case 255 :
{
// This is the state used for "not processing packets anymore" when we receive a bad packet from a client.
// Do not output anything (the caller will do that for us), just return failure
return false ;
}
2013-10-30 23:24:46 +01:00
} // switch (m_State)
2014-01-17 10:12:04 +01:00
// Unknown packet type, report to the ClientHandle:
2013-10-30 23:24:46 +01:00
m_Client -> PacketUnknown ( a_PacketType );
2014-01-16 15:26:58 +01:00
return false ;
2013-10-30 23:24:46 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketStatusPing ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
Int64 Timestamp ;
2013-12-13 17:53:00 +01:00
a_ByteBuffer . ReadBEInt64 ( Timestamp );
2013-10-31 23:48:43 +01:00
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x01 ); // Ping packet
Pkt . WriteInt64 ( Timestamp );
2013-10-31 23:48:43 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketStatusRequest ( cByteBuffer & a_ByteBuffer )
2013-10-30 23:24:46 +01:00
{
// Send the response:
AString Response = "{ \" version \" :{ \" name \" : \" 1.7.2 \" , \" protocol \" :4}, \" players \" :{" ;
AppendPrintf ( Response , " \" max \" :%u, \" online \" :%u, \" sample \" :[]}," ,
cRoot :: Get () -> GetServer () -> GetMaxPlayers (),
cRoot :: Get () -> GetServer () -> GetNumPlayers ()
);
2014-01-07 16:53:40 +00:00
AppendPrintf ( Response , " \" description \" :{ \" text \" : \" %s \" }," ,
2013-10-30 23:24:46 +01:00
cRoot :: Get () -> GetServer () -> GetDescription (). c_str ()
);
2014-01-07 09:31:06 -06:00
AppendPrintf ( Response , " \" favicon \" : \" data:image/png;base64,%s \" " ,
2014-01-07 09:49:52 -06:00
cRoot :: Get () -> GetServer () -> GetFaviconData (). c_str ()
2014-01-07 09:31:06 -06:00
);
2013-10-30 23:24:46 +01:00
Response . append ( "}" );
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x00 ); // Response packet
Pkt . WriteString ( Response );
2013-10-30 23:24:46 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketLoginEncryptionResponse ( cByteBuffer & a_ByteBuffer )
2013-10-30 23:38:55 +01:00
{
2014-01-28 23:53:07 +01:00
short EncKeyLength , EncNonceLength ;
a_ByteBuffer . ReadBEShort ( EncKeyLength );
AString EncKey ;
if ( ! a_ByteBuffer . ReadString ( EncKey , EncKeyLength ))
{
return ;
}
a_ByteBuffer . ReadBEShort ( EncNonceLength );
AString EncNonce ;
if ( ! a_ByteBuffer . ReadString ( EncNonce , EncNonceLength ))
{
return ;
}
if (( EncKeyLength > MAX_ENC_LEN ) || ( EncNonceLength > MAX_ENC_LEN ))
{
LOGD ( "Too long encryption" );
m_Client -> Kick ( "Hacked client" );
return ;
}
// Decrypt EncNonce using privkey
cRSAPrivateKey & rsaDecryptor = cRoot :: Get () -> GetServer () -> GetPrivateKey ();
Int32 DecryptedNonce [ MAX_ENC_LEN / sizeof ( Int32 )];
int res = rsaDecryptor . Decrypt (( const Byte * ) EncNonce . data (), EncNonce . size (), ( Byte * ) DecryptedNonce , sizeof ( DecryptedNonce ));
if ( res != 4 )
{
LOGD ( "Bad nonce length: got %d, exp %d" , res , 4 );
m_Client -> Kick ( "Hacked client" );
return ;
}
if ( ntohl ( DecryptedNonce [ 0 ]) != ( 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 (( const Byte * ) EncKey . data (), EncKey . size (), DecryptedKey , sizeof ( DecryptedKey ));
if ( res != 16 )
{
LOGD ( "Bad key length" );
m_Client -> Kick ( "Hacked client" );
return ;
}
StartEncryption ( DecryptedKey );
// Send login success:
{
cPacketizer Pkt ( * this , 0x02 ); // Login success packet
Pkt . WriteString ( Printf ( "%d" , m_Client -> GetUniqueID ())); // TODO: proper UUID
Pkt . WriteString ( m_Client -> GetUsername ());
}
m_State = 3 ; // State = Game
m_Client -> HandleLogin ( 4 , m_Client -> GetUsername ());
2013-10-31 23:48:43 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketLoginStart ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
AString Username ;
2013-12-13 17:53:00 +01:00
a_ByteBuffer . ReadVarUTF8String ( Username );
2013-10-30 23:38:55 +01:00
2014-01-13 20:32:15 +01:00
if ( ! m_Client -> HandleHandshake ( Username ))
{
// The client is not welcome here, they have been sent a Kick packet already
return ;
}
2014-01-28 23:53:07 +01:00
// If auth is required, then send the encryption request:
2014-01-29 09:56:31 +01:00
if ( cRoot :: Get () -> GetServer () -> ShouldAuthenticate ())
2014-01-28 23:53:07 +01:00
{
cPacketizer Pkt ( * this , 0x01 );
Pkt . WriteString ( cRoot :: Get () -> GetServer () -> GetServerID ());
const AString & PubKeyDer = cRoot :: Get () -> GetServer () -> GetPublicKeyDER ();
Pkt . WriteShort ( PubKeyDer . size ());
Pkt . WriteBuf ( PubKeyDer . data (), PubKeyDer . size ());
Pkt . WriteShort ( 4 );
Pkt . WriteInt (( int )( intptr_t ) this ); // Using 'this' as the cryptographic nonce, so that we don't have to generate one each time :)
m_Client -> SetUsername ( Username );
return ;
}
2013-10-31 23:48:43 +01:00
// Send login success:
2013-11-03 11:58:49 +01:00
{
cPacketizer Pkt ( * this , 0x02 ); // Login success packet
Pkt . WriteString ( Printf ( "%d" , m_Client -> GetUniqueID ())); // TODO: proper UUID
Pkt . WriteString ( Username );
}
2013-10-31 23:48:43 +01:00
2013-11-03 11:58:49 +01:00
m_State = 3 ; // State = Game
2013-10-31 23:48:43 +01:00
m_Client -> HandleLogin ( 4 , Username );
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketAnimation ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
2013-12-13 17:53:00 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEInt , int , EntityID );
HANDLE_READ ( a_ByteBuffer , ReadByte , Byte , Animation );
2013-10-31 23:48:43 +01:00
m_Client -> HandleAnimation ( Animation );
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketBlockDig ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
2013-12-13 17:53:00 +01:00
HANDLE_READ ( a_ByteBuffer , ReadByte , Byte , Status );
HANDLE_READ ( a_ByteBuffer , ReadBEInt , int , BlockX );
HANDLE_READ ( a_ByteBuffer , ReadByte , Byte , BlockY );
HANDLE_READ ( a_ByteBuffer , ReadBEInt , int , BlockZ );
2014-02-04 22:15:01 +01:00
HANDLE_READ ( a_ByteBuffer , ReadChar , char , Face );
2014-02-04 10:59:05 -08:00
m_Client -> HandleLeftClick ( BlockX , BlockY , BlockZ , static_cast < eBlockFace > ( Face ), Status );
2013-10-31 23:48:43 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketBlockPlace ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
2013-12-13 17:53:00 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEInt , int , BlockX );
HANDLE_READ ( a_ByteBuffer , ReadByte , Byte , BlockY );
HANDLE_READ ( a_ByteBuffer , ReadBEInt , int , BlockZ );
2014-02-04 22:15:01 +01:00
HANDLE_READ ( a_ByteBuffer , ReadChar , char , Face );
2013-12-02 09:32:28 -07:00
cItem Item ;
2013-12-13 17:53:00 +01:00
ReadItem ( a_ByteBuffer , Item );
2013-12-01 20:40:12 -07:00
2013-12-13 17:53:00 +01:00
HANDLE_READ ( a_ByteBuffer , ReadByte , Byte , CursorX );
HANDLE_READ ( a_ByteBuffer , ReadByte , Byte , CursorY );
HANDLE_READ ( a_ByteBuffer , ReadByte , Byte , CursorZ );
2014-02-04 10:59:05 -08:00
m_Client -> HandleRightClick ( BlockX , BlockY , BlockZ , static_cast < eBlockFace > ( Face ), CursorX , CursorY , CursorZ , m_Client -> GetPlayer () -> GetEquippedItem ());
2013-10-31 23:48:43 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketChatMessage ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
2013-12-13 17:53:00 +01:00
HANDLE_READ ( a_ByteBuffer , ReadVarUTF8String , AString , Message );
2013-10-31 23:48:43 +01:00
m_Client -> HandleChat ( Message );
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketClientSettings ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
2013-12-13 17:53:00 +01:00
HANDLE_READ ( a_ByteBuffer , ReadVarUTF8String , AString , Locale );
HANDLE_READ ( a_ByteBuffer , ReadByte , Byte , ViewDistance );
HANDLE_READ ( a_ByteBuffer , ReadByte , Byte , ChatFlags );
HANDLE_READ ( a_ByteBuffer , ReadByte , Byte , ChatColors );
HANDLE_READ ( a_ByteBuffer , ReadByte , Byte , Difficulty );
HANDLE_READ ( a_ByteBuffer , ReadByte , Byte , ShowCape );
2014-02-16 13:26:07 +01:00
m_Client -> SetLocale ( Locale );
2013-11-03 11:58:49 +01:00
// TODO: handle in m_Client
2013-10-31 23:48:43 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketClientStatus ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
2013-12-13 17:53:00 +01:00
HANDLE_READ ( a_ByteBuffer , ReadByte , Byte , ActionID );
2013-11-03 21:34:36 +01:00
switch ( ActionID )
{
case 0 :
{
// Respawn
m_Client -> HandleRespawn ();
break ;
}
case 1 :
{
// Request stats
// TODO
break ;
}
case 2 :
{
// Open Inventory achievement
// TODO
break ;
}
}
2013-10-31 23:48:43 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketCreativeInventoryAction ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
2013-12-13 17:53:00 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEShort , short , SlotNum );
2013-11-06 20:48:35 +01:00
cItem Item ;
2013-12-13 17:53:00 +01:00
if ( ! ReadItem ( a_ByteBuffer , Item ))
2013-11-06 20:48:35 +01:00
{
return ;
}
m_Client -> HandleCreativeInventory ( SlotNum , Item );
2013-10-31 23:48:43 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketEntityAction ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
2013-12-13 17:53:00 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEInt , int , PlayerID );
HANDLE_READ ( a_ByteBuffer , ReadByte , Byte , Action );
HANDLE_READ ( a_ByteBuffer , ReadBEInt , int , JumpBoost );
2013-11-06 20:48:35 +01:00
m_Client -> HandleEntityAction ( PlayerID , Action );
2013-10-31 23:48:43 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketKeepAlive ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
2013-12-13 17:53:00 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEInt , int , KeepAliveID );
2013-11-06 22:27:09 +01:00
m_Client -> HandleKeepAlive ( KeepAliveID );
2013-10-31 23:48:43 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketPlayer ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
2013-12-13 17:53:00 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBool , bool , IsOnGround );
2013-11-06 22:27:09 +01:00
// TODO: m_Client->HandlePlayerOnGround(IsOnGround);
2013-10-31 23:48:43 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketPlayerAbilities ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
2013-12-13 17:53:00 +01:00
HANDLE_READ ( a_ByteBuffer , ReadByte , Byte , Flags );
HANDLE_READ ( a_ByteBuffer , ReadBEFloat , float , FlyingSpeed );
HANDLE_READ ( a_ByteBuffer , ReadBEFloat , float , WalkingSpeed );
2013-12-15 15:11:59 +01:00
2013-12-19 20:53:47 +00:00
bool IsFlying = false , CanFly = false ;
2013-12-15 15:11:59 +01:00
if (( Flags & 2 ) != 0 )
{
IsFlying = true ;
}
if (( Flags & 4 ) != 0 )
{
CanFly = true ;
}
2013-12-19 20:53:47 +00:00
2013-12-15 15:11:59 +01:00
m_Client -> HandlePlayerAbilities ( CanFly , IsFlying , FlyingSpeed , WalkingSpeed );
2013-10-31 23:48:43 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketPlayerLook ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
2013-12-13 17:53:00 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEFloat , float , Yaw );
HANDLE_READ ( a_ByteBuffer , ReadBEFloat , float , Pitch );
HANDLE_READ ( a_ByteBuffer , ReadBool , bool , IsOnGround );
2013-11-06 22:27:09 +01:00
m_Client -> HandlePlayerLook ( Yaw , Pitch , IsOnGround );
2013-10-31 23:48:43 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketPlayerPos ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
2013-12-13 17:53:00 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEDouble , double , PosX );
HANDLE_READ ( a_ByteBuffer , ReadBEDouble , double , PosY );
HANDLE_READ ( a_ByteBuffer , ReadBEDouble , double , Stance );
HANDLE_READ ( a_ByteBuffer , ReadBEDouble , double , PosZ );
HANDLE_READ ( a_ByteBuffer , ReadBool , bool , IsOnGround );
2013-11-06 22:27:09 +01:00
m_Client -> HandlePlayerPos ( PosX , PosY , PosZ , Stance , IsOnGround );
2013-10-31 23:48:43 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketPlayerPosLook ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
2013-12-13 17:53:00 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEDouble , double , PosX );
HANDLE_READ ( a_ByteBuffer , ReadBEDouble , double , PosY );
HANDLE_READ ( a_ByteBuffer , ReadBEDouble , double , Stance );
HANDLE_READ ( a_ByteBuffer , ReadBEDouble , double , PosZ );
HANDLE_READ ( a_ByteBuffer , ReadBEFloat , float , Yaw );
HANDLE_READ ( a_ByteBuffer , ReadBEFloat , float , Pitch );
HANDLE_READ ( a_ByteBuffer , ReadBool , bool , IsOnGround );
2013-11-06 22:27:09 +01:00
m_Client -> HandlePlayerMoveLook ( PosX , PosY , PosZ , Stance , Yaw , Pitch , IsOnGround );
2013-10-31 23:48:43 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketPluginMessage ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
2013-12-13 17:53:00 +01:00
HANDLE_READ ( a_ByteBuffer , ReadVarUTF8String , AString , Channel );
HANDLE_READ ( a_ByteBuffer , ReadBEShort , short , Length );
2013-11-06 22:27:09 +01:00
AString Data ;
2013-12-13 17:53:00 +01:00
a_ByteBuffer . ReadString ( Data , Length );
2014-01-07 17:47:05 +01:00
m_Client -> HandlePluginMessage ( Channel , Data );
2013-10-31 23:48:43 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketSlotSelect ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
2013-12-13 17:53:00 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEShort , short , SlotNum );
2013-11-06 22:27:09 +01:00
m_Client -> HandleSlotSelected ( SlotNum );
2013-10-31 23:48:43 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketSteerVehicle ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
2013-12-13 17:53:00 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEFloat , float , Forward );
HANDLE_READ ( a_ByteBuffer , ReadBEFloat , float , Sideways );
HANDLE_READ ( a_ByteBuffer , ReadBool , bool , ShouldJump );
HANDLE_READ ( a_ByteBuffer , ReadBool , bool , ShouldUnmount );
2013-11-06 22:27:09 +01:00
if ( ShouldUnmount )
{
m_Client -> HandleUnmount ();
}
else
{
m_Client -> HandleSteerVehicle ( Forward , Sideways );
}
2013-10-31 23:48:43 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketTabComplete ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
2013-12-13 17:53:00 +01:00
HANDLE_READ ( a_ByteBuffer , ReadVarUTF8String , AString , Text );
2013-11-06 22:27:09 +01:00
m_Client -> HandleTabCompletion ( Text );
2013-10-31 23:48:43 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketUpdateSign ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
2013-12-13 17:53:00 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEInt , int , BlockX );
HANDLE_READ ( a_ByteBuffer , ReadBEShort , short , BlockY );
HANDLE_READ ( a_ByteBuffer , ReadBEInt , int , BlockZ );
HANDLE_READ ( a_ByteBuffer , ReadVarUTF8String , AString , Line1 );
HANDLE_READ ( a_ByteBuffer , ReadVarUTF8String , AString , Line2 );
HANDLE_READ ( a_ByteBuffer , ReadVarUTF8String , AString , Line3 );
HANDLE_READ ( a_ByteBuffer , ReadVarUTF8String , AString , Line4 );
2013-11-06 22:27:09 +01:00
m_Client -> HandleUpdateSign ( BlockX , BlockY , BlockZ , Line1 , Line2 , Line3 , Line4 );
2013-10-31 23:48:43 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketUseEntity ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
2013-12-13 17:53:00 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEInt , int , EntityID );
HANDLE_READ ( a_ByteBuffer , ReadByte , Byte , MouseButton );
2013-11-08 21:03:51 +01:00
m_Client -> HandleUseEntity ( EntityID , ( MouseButton == 1 ));
2013-10-31 23:48:43 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketWindowClick ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
2013-12-13 17:53:00 +01:00
HANDLE_READ ( a_ByteBuffer , ReadChar , char , WindowID );
HANDLE_READ ( a_ByteBuffer , ReadBEShort , short , SlotNum );
HANDLE_READ ( a_ByteBuffer , ReadByte , Byte , Button );
HANDLE_READ ( a_ByteBuffer , ReadBEShort , short , TransactionID );
HANDLE_READ ( a_ByteBuffer , ReadByte , Byte , Mode );
2013-11-06 22:27:09 +01:00
cItem Item ;
2013-12-13 17:53:00 +01:00
ReadItem ( a_ByteBuffer , Item );
2013-11-06 22:27:09 +01:00
// 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 ;
2013-12-13 17:53:00 +01:00
default :
{
LOGWARNING ( "Unhandled window click mode / button combination: %d (0x%x)" , ( Mode << 8 ) | Button , ( Mode << 8 ) | Button );
Action = caUnknown ;
break ;
}
2013-11-06 22:27:09 +01:00
}
m_Client -> HandleWindowClick ( WindowID , SlotNum , Action , Item );
2013-10-31 23:48:43 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketWindowClose ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
2013-12-13 17:53:00 +01:00
HANDLE_READ ( a_ByteBuffer , ReadChar , char , WindowID );
2013-11-06 22:27:09 +01:00
m_Client -> HandleWindowClose ( WindowID );
2013-10-30 23:38:55 +01:00
}
2013-10-30 23:24:46 +01:00
void cProtocol172 :: WritePacket ( cByteBuffer & a_Packet )
{
cCSLock Lock ( m_CSPacket );
AString Pkt ;
a_Packet . ReadAll ( Pkt );
WriteVarInt ( Pkt . size ());
SendData ( Pkt . data (), Pkt . size ());
Flush ();
}
void cProtocol172 :: SendData ( const char * a_Data , int a_Size )
{
if ( m_IsEncrypted )
{
2014-01-23 23:35:23 +01:00
Byte Encrypted [ 8192 ]; // Larger buffer, we may be sending lots of data (chunks)
2013-10-30 23:24:46 +01:00
while ( a_Size > 0 )
{
2014-02-05 13:54:47 +01:00
size_t NumBytes = (( size_t ) a_Size > sizeof ( Encrypted )) ? sizeof ( Encrypted ) : ( size_t ) a_Size ;
2014-01-23 23:35:23 +01:00
m_Encryptor . ProcessData ( Encrypted , ( Byte * ) a_Data , NumBytes );
2013-10-30 23:24:46 +01:00
m_Client -> SendData (( const char * ) Encrypted , NumBytes );
a_Size -= NumBytes ;
a_Data += NumBytes ;
}
}
else
{
m_Client -> SendData ( a_Data , a_Size );
}
}
2013-11-01 16:20:15 +01:00
2013-12-13 17:53:00 +01:00
bool cProtocol172 :: ReadItem ( cByteBuffer & a_ByteBuffer , cItem & a_Item )
2013-11-06 20:48:35 +01:00
{
2013-12-13 17:53:00 +01:00
HANDLE_PACKET_READ ( a_ByteBuffer , ReadBEShort , short , ItemType );
2013-11-06 20:48:35 +01:00
if ( ItemType == - 1 )
{
// The item is empty, no more data follows
a_Item . Empty ();
return true ;
}
a_Item . m_ItemType = ItemType ;
2013-12-13 17:53:00 +01:00
HANDLE_PACKET_READ ( a_ByteBuffer , ReadChar , char , ItemCount );
HANDLE_PACKET_READ ( a_ByteBuffer , ReadBEShort , short , ItemDamage );
2013-11-06 20:48:35 +01:00
a_Item . m_ItemCount = ItemCount ;
a_Item . m_ItemDamage = ItemDamage ;
if ( ItemCount <= 0 )
{
a_Item . Empty ();
}
2013-12-13 17:53:00 +01:00
HANDLE_PACKET_READ ( a_ByteBuffer , ReadBEShort , short , MetadataLength );
2013-11-06 20:48:35 +01:00
if ( MetadataLength <= 0 )
{
return true ;
}
// Read the metadata
AString Metadata ;
2013-12-13 17:53:00 +01:00
if ( ! a_ByteBuffer . ReadString ( Metadata , MetadataLength ))
2013-11-06 20:48:35 +01:00
{
return false ;
}
ParseItemMetadata ( a_Item , Metadata );
return true ;
}
void cProtocol172 :: 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 );
LOGWARNING ( "Cannot unGZIP item metadata (%u bytes): \n %s" , a_Metadata . size (), HexDump . c_str ());
return ;
}
// Parse into NBT:
cParsedNBT NBT ( Uncompressed . data (), Uncompressed . size ());
if ( ! NBT . IsValid ())
{
AString HexDump ;
CreateHexDump ( HexDump , Uncompressed . data (), Uncompressed . size (), 16 );
LOGWARNING ( "Cannot parse NBT item metadata: (%u bytes) \n %s" , Uncompressed . size (), HexDump . c_str ());
return ;
}
2014-01-15 22:38:03 +00:00
// Load enchantments and custom display names from the NBT data:
2013-11-06 20:48:35 +01:00
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" )
)
)
{
2014-01-19 08:52:45 -08:00
EnchantmentSerializer :: ParseFromNBT ( a_Item . m_Enchantments , NBT , tag );
2013-11-06 20:48:35 +01:00
}
2014-01-15 22:38:03 +00:00
else if (( NBT . GetType ( tag ) == TAG_Compound ) && ( NBT . GetName ( tag ) == "display" )) // Custom name and lore tag
{
for ( int displaytag = NBT . GetFirstChild ( tag ); displaytag >= 0 ; displaytag = NBT . GetNextSibling ( displaytag ))
{
if (( NBT . GetType ( displaytag ) == TAG_String ) && ( NBT . GetName ( displaytag ) == "Name" )) // Custon name tag
{
a_Item . m_CustomName = NBT . GetString ( displaytag );
}
else if (( NBT . GetType ( displaytag ) == TAG_List ) && ( NBT . GetName ( displaytag ) == "Lore" )) // Lore tag
{
AString Lore ;
for ( int loretag = NBT . GetFirstChild ( displaytag ); loretag >= 0 ; loretag = NBT . GetNextSibling ( loretag )) // Loop through array of strings
{
2014-01-16 22:30:57 +00:00
AppendPrintf ( Lore , "%s`" , NBT . GetString ( loretag ). c_str ()); // Append the lore with a newline, used internally by MCS to display a new line in the client; don't forget to c_str ;)
2014-01-15 22:38:03 +00:00
}
a_Item . m_Lore = Lore ;
}
}
}
2013-11-06 20:48:35 +01:00
}
}
2014-01-28 23:53:07 +01:00
void cProtocol172 :: StartEncryption ( const Byte * a_Key )
{
m_Encryptor . Init ( a_Key , a_Key );
m_Decryptor . Init ( a_Key , a_Key );
m_IsEncrypted = true ;
// Prepare the m_AuthServerID:
cSHA1Checksum Checksum ;
const AString & ServerID = cRoot :: Get () -> GetServer () -> GetServerID ();
Checksum . Update (( const Byte * ) ServerID . c_str (), ServerID . length ());
Checksum . Update ( a_Key , 16 );
Checksum . Update (( const Byte * ) cRoot :: Get () -> GetServer () -> GetPublicKeyDER (). data (), cRoot :: Get () -> GetServer () -> GetPublicKeyDER (). size ());
Byte Digest [ 20 ];
Checksum . Finalize ( Digest );
cSHA1Checksum :: DigestToJava ( Digest , m_AuthServerID );
}
2014-02-15 23:16:44 +01:00
void cProtocol172 :: AddChatPartStyle ( Json :: Value & a_Value , const AString & a_PartStyle )
{
size_t len = a_PartStyle . length ();
for ( size_t i = 0 ; i < len ; i ++ )
{
switch ( a_PartStyle [ i ])
{
case 'b' :
{
// bold
a_Value [ "bold" ] = Json :: Value ( true );
break ;
}
case 'i' :
{
// italic
a_Value [ "italic" ] = Json :: Value ( true );
break ;
}
case 'u' :
{
// Underlined
a_Value [ "underlined" ] = Json :: Value ( true );
break ;
}
case 's' :
{
// strikethrough
a_Value [ "strikethrough" ] = Json :: Value ( true );
break ;
}
case 'o' :
{
// obfuscated
a_Value [ "obfuscated" ] = Json :: Value ( true );
break ;
}
case '@' :
{
// Color, specified by the next char:
i ++ ;
if ( i >= len )
{
// String too short, didn't contain a color
break ;
}
switch ( a_PartStyle [ i ])
{
case '0' : a_Value [ "color" ] = Json :: Value ( "black" ); break ;
case '1' : a_Value [ "color" ] = Json :: Value ( "dark_blue" ); break ;
case '2' : a_Value [ "color" ] = Json :: Value ( "dark_green" ); break ;
case '3' : a_Value [ "color" ] = Json :: Value ( "dark_aqua" ); break ;
case '4' : a_Value [ "color" ] = Json :: Value ( "dark_red" ); break ;
case '5' : a_Value [ "color" ] = Json :: Value ( "dark_purple" ); break ;
case '6' : a_Value [ "color" ] = Json :: Value ( "gold" ); break ;
case '7' : a_Value [ "color" ] = Json :: Value ( "gray" ); break ;
case '8' : a_Value [ "color" ] = Json :: Value ( "dark_gray" ); break ;
case '9' : a_Value [ "color" ] = Json :: Value ( "blue" ); break ;
case 'a' : a_Value [ "color" ] = Json :: Value ( "green" ); break ;
case 'b' : a_Value [ "color" ] = Json :: Value ( "aqua" ); break ;
case 'c' : a_Value [ "color" ] = Json :: Value ( "red" ); break ;
case 'd' : a_Value [ "color" ] = Json :: Value ( "light_purple" ); break ;
case 'e' : a_Value [ "color" ] = Json :: Value ( "yellow" ); break ;
case 'f' : a_Value [ "color" ] = Json :: Value ( "white" ); break ;
} // switch (color)
} // case '@'
} // switch (Style[i])
} // for i - a_PartStyle[]
}
2013-11-03 11:58:49 +01:00
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cProtocol172::cPacketizer:
cProtocol172 :: cPacketizer ::~ cPacketizer ()
{
AString DataToSend ;
// Send the packet length
UInt32 PacketLen = m_Out . GetUsedSpace ();
m_Protocol . m_OutPacketLenBuffer . WriteVarInt ( PacketLen );
m_Protocol . m_OutPacketLenBuffer . ReadAll ( DataToSend );
m_Protocol . SendData ( DataToSend . data (), DataToSend . size ());
m_Protocol . m_OutPacketLenBuffer . CommitRead ();
// Send the packet data:
m_Out . ReadAll ( DataToSend );
m_Protocol . SendData ( DataToSend . data (), DataToSend . size ());
m_Out . CommitRead ();
2014-01-24 23:03:48 +01:00
// Log the comm into logfile:
2014-01-26 17:54:18 +01:00
if ( g_ShouldLogCommOut )
2014-01-24 23:03:48 +01:00
{
AString Hex ;
ASSERT ( DataToSend . size () > 0 );
CreateHexDump ( Hex , DataToSend . data () + 1 , DataToSend . size () - 1 , 16 );
m_Protocol . m_CommLogFile . Printf ( "Outgoing packet: type %d (0x%x), length %u (0x%x), state %d. Payload: \n %s \n " ,
DataToSend [ 0 ], DataToSend [ 0 ], PacketLen , PacketLen , m_Protocol . m_State , Hex . c_str ()
);
}
2013-11-03 11:58:49 +01:00
}
void cProtocol172 :: cPacketizer :: 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 );
2014-01-15 22:38:03 +00:00
if ( a_Item . m_Enchantments . IsEmpty () && a_Item . IsBothNameAndLoreEmpty ())
2013-11-03 11:58:49 +01:00
{
WriteShort ( - 1 );
return ;
}
2014-01-15 22:38:03 +00:00
// Send the enchantments and custom names:
2013-11-03 11:58:49 +01:00
cFastNBTWriter Writer ;
2014-01-15 22:38:03 +00:00
if ( ! a_Item . m_Enchantments . IsEmpty ())
{
const char * TagName = ( a_Item . m_ItemType == E_ITEM_BOOK ) ? "StoredEnchantments" : "ench" ;
2014-01-19 08:52:45 -08:00
EnchantmentSerializer :: WriteToNBTCompound ( a_Item . m_Enchantments , Writer , TagName );
2014-01-15 22:38:03 +00:00
}
if ( ! a_Item . IsBothNameAndLoreEmpty ())
{
Writer . BeginCompound ( "display" );
if ( ! a_Item . IsCustomNameEmpty ())
{
Writer . AddString ( "Name" , a_Item . m_CustomName . c_str ());
}
if ( ! a_Item . IsLoreEmpty ())
{
Writer . BeginList ( "Lore" , TAG_String );
2014-01-16 22:30:57 +00:00
AStringVector Decls = StringSplit ( a_Item . m_Lore , "`" );
2014-01-15 22:38:03 +00:00
for ( AStringVector :: const_iterator itr = Decls . begin (), end = Decls . end (); itr != end ; ++ itr )
{
if ( itr -> empty ())
{
2014-01-16 22:30:57 +00:00
// The decl is empty (two `s), ignore
2014-01-15 22:38:03 +00:00
continue ;
}
Writer . AddString ( "" , itr -> c_str ());
}
Writer . EndList ();
}
Writer . EndCompound ();
}
2013-11-03 11:58:49 +01:00
Writer . Finish ();
AString Compressed ;
CompressStringGZIP ( Writer . GetResult (). data (), Writer . GetResult (). size (), Compressed );
WriteShort ( Compressed . size ());
WriteBuf ( Compressed . data (), Compressed . size ());
}
2014-01-19 13:25:35 +00:00
void cProtocol172 :: cPacketizer :: WriteBlockEntity ( const cBlockEntity & a_BlockEntity )
2014-01-19 00:54:38 +00:00
{
2014-01-19 13:25:35 +00:00
cFastNBTWriter Writer ;
switch ( a_BlockEntity . GetBlockType ())
{
case E_BLOCK_COMMAND_BLOCK :
{
cCommandBlockEntity & CommandBlockEntity = ( cCommandBlockEntity & ) a_BlockEntity ;
Writer . AddByte ( "TrackOutput" , 1 ); // Neither I nor the MC wiki has any idea about this
Writer . AddInt ( "SuccessCount" , CommandBlockEntity . GetResult ());
Writer . AddInt ( "x" , CommandBlockEntity . GetPosX ());
Writer . AddInt ( "y" , CommandBlockEntity . GetPosY ());
Writer . AddInt ( "z" , CommandBlockEntity . GetPosZ ());
Writer . AddString ( "Command" , CommandBlockEntity . GetCommand (). c_str ());
// You can set custom names for windows in Vanilla
// For a command block, this would be the 'name' prepended to anything it outputs into global chat
// MCS doesn't have this, so just leave it @ '@'. (geddit?)
Writer . AddString ( "CustomName" , "@" );
Writer . AddString ( "id" , "Control" ); // "Tile Entity ID" - MC wiki; vanilla server always seems to send this though
if ( ! CommandBlockEntity . GetLastOutput (). empty ())
{
AString Output ;
Printf ( Output , "{ \" text \" : \" %s \" }" , CommandBlockEntity . GetLastOutput (). c_str ());
Writer . AddString ( "LastOutput" , Output . c_str ());
}
break ;
}
default : break ;
}
Writer . Finish ();
2014-01-19 00:54:38 +00:00
AString Compressed ;
2014-01-19 13:25:35 +00:00
CompressStringGZIP ( Writer . GetResult (). data (), Writer . GetResult (). size (), Compressed );
2014-01-19 00:54:38 +00:00
WriteShort ( Compressed . size ());
WriteBuf ( Compressed . data (), Compressed . size ());
}
2013-11-03 11:58:49 +01:00
void cProtocol172 :: cPacketizer :: WriteByteAngle ( double a_Angle )
{
WriteByte (( char )( 255 * a_Angle / 360 ));
}
void cProtocol172 :: cPacketizer :: WriteFPInt ( double a_Value )
{
int Value = ( int )( a_Value * 32 );
WriteInt ( Value );
}
2013-11-04 21:20:36 +01:00
void cProtocol172 :: cPacketizer :: WriteEntityMetadata ( const cEntity & a_Entity )
{
// Common metadata:
Byte Flags = 0 ;
if ( a_Entity . IsOnFire ())
{
Flags |= 0x01 ;
}
if ( a_Entity . IsCrouched ())
{
Flags |= 0x02 ;
}
if ( a_Entity . IsSprinting ())
{
Flags |= 0x08 ;
}
if ( a_Entity . IsRclking ())
{
Flags |= 0x10 ;
}
if ( a_Entity . IsInvisible ())
{
Flags |= 0x20 ;
}
WriteByte ( 0 ); // Byte(0) + index 0
WriteByte ( Flags );
switch ( a_Entity . GetEntityType ())
{
case cEntity :: etPlayer : break ; // TODO?
case cEntity :: etPickup :
{
WriteByte (( 5 << 5 ) | 10 ); // Slot(5) + index 10
WriteItem ((( const cPickup & ) a_Entity ). GetItem ());
break ;
}
2013-11-10 20:48:12 +00:00
case cEntity :: etMinecart :
{
WriteByte ( 0x51 );
// The following expression makes Minecarts shake more with less health or higher damage taken
// It gets half the maximum health, and takes it away from the current health minus the half health:
/* Health: 5 | 3 - (5 - 3) = 1 (shake power)
Health: 3 | 3 - (3 - 3) = 3
Health: 1 | 3 - (1 - 3) = 5
*/
WriteInt (((( a_Entity . GetMaxHealth () / 2 ) - ( a_Entity . GetHealth () - ( a_Entity . GetMaxHealth () / 2 ))) * (( const cMinecart & ) a_Entity ). LastDamage ()) * 4 );
WriteByte ( 0x52 );
WriteInt ( 1 ); // Shaking direction, doesn't seem to affect anything
WriteByte ( 0x73 );
WriteFloat (( float )((( const cMinecart & ) a_Entity ). LastDamage () + 10 )); // Damage taken / shake effect multiplyer
2014-01-12 15:27:50 +01:00
if ((( cMinecart & ) a_Entity ). GetPayload () == cMinecart :: mpNone )
{
2014-01-12 18:04:41 +01:00
cRideableMinecart & RideableMinecart = (( cRideableMinecart & ) a_Entity );
if ( ! RideableMinecart . GetContent (). IsEmpty ())
2014-01-12 15:27:50 +01:00
{
WriteByte ( 0x54 );
2014-01-12 18:04:41 +01:00
int Content = RideableMinecart . GetContent (). m_ItemType ;
Content |= RideableMinecart . GetContent (). m_ItemDamage << 8 ;
2014-01-12 15:27:50 +01:00
WriteInt ( Content );
WriteByte ( 0x55 );
2014-01-12 18:04:41 +01:00
WriteInt ( RideableMinecart . GetBlockHeight ());
2014-01-12 15:27:50 +01:00
WriteByte ( 0x56 );
WriteByte ( 1 );
}
}
2014-01-12 18:04:41 +01:00
else if ((( cMinecart & ) a_Entity ). GetPayload () == cMinecart :: mpFurnace )
2013-11-10 20:48:12 +00:00
{
WriteByte ( 0x10 );
WriteByte ((( const cMinecartWithFurnace & ) a_Entity ). IsFueled () ? 1 : 0 );
}
break ;
}
case cEntity :: etProjectile :
{
if ((( cProjectileEntity & ) a_Entity ). GetProjectileKind () == cProjectileEntity :: pkArrow )
{
WriteByte ( 0x10 );
WriteByte ((( const cArrowEntity & ) a_Entity ). IsCritical () ? 1 : 0 );
}
break ;
}
2013-11-04 21:20:36 +01:00
case cEntity :: etMonster :
{
WriteMobMetadata (( const cMonster & ) a_Entity );
break ;
}
}
}
void cProtocol172 :: cPacketizer :: WriteMobMetadata ( const cMonster & a_Mob )
{
2013-11-08 20:56:19 +01:00
switch ( a_Mob . GetMobType ())
{
case cMonster :: mtCreeper :
{
WriteByte ( 0x10 );
WriteByte ((( const cCreeper & ) a_Mob ). IsBlowing () ? 1 : - 1 );
WriteByte ( 0x11 );
WriteByte ((( const cCreeper & ) a_Mob ). IsCharged () ? 1 : 0 );
break ;
}
case cMonster :: mtBat :
{
WriteByte ( 0x10 );
WriteByte ((( const cBat & ) a_Mob ). IsHanging () ? 1 : 0 );
break ;
}
case cMonster :: mtPig :
{
WriteByte ( 0x10 );
WriteByte ((( const cPig & ) a_Mob ). IsSaddled () ? 1 : 0 );
break ;
}
case cMonster :: mtVillager :
{
WriteByte ( 0x50 );
WriteInt ((( const cVillager & ) a_Mob ). GetVilType ());
break ;
}
case cMonster :: mtZombie :
{
WriteByte ( 0x0c );
WriteByte ((( const cZombie & ) a_Mob ). IsBaby () ? 1 : 0 );
WriteByte ( 0x0d );
WriteByte ((( const cZombie & ) a_Mob ). IsVillagerZombie () ? 1 : 0 );
WriteByte ( 0x0e );
WriteByte ((( const cZombie & ) a_Mob ). IsConverting () ? 1 : 0 );
break ;
}
case cMonster :: mtGhast :
{
WriteByte ( 0x10 );
WriteByte ((( const cGhast & ) a_Mob ). IsCharging ());
break ;
}
case cMonster :: mtWolf :
{
const cWolf & Wolf = ( const cWolf & ) a_Mob ;
Byte WolfStatus = 0 ;
if ( Wolf . IsSitting ())
{
WolfStatus |= 0x1 ;
}
if ( Wolf . IsAngry ())
{
WolfStatus |= 0x2 ;
}
if ( Wolf . IsTame ())
{
WolfStatus |= 0x4 ;
}
WriteByte ( 0x10 );
WriteByte ( WolfStatus );
WriteByte ( 0x72 );
WriteFloat (( float )( a_Mob . GetHealth ()));
WriteByte ( 0x13 );
WriteByte ( Wolf . IsBegging () ? 1 : 0 );
2013-11-10 20:12:30 +01:00
WriteByte ( 0x14 );
WriteByte ( Wolf . GetCollarColor ());
2013-11-08 20:56:19 +01:00
break ;
}
case cMonster :: mtSheep :
{
WriteByte ( 0x10 );
Byte SheepMetadata = 0 ;
SheepMetadata = (( const cSheep & ) a_Mob ). GetFurColor ();
if ((( const cSheep & ) a_Mob ). IsSheared ())
{
SheepMetadata |= 0x10 ;
}
WriteByte ( SheepMetadata );
break ;
}
case cMonster :: mtEnderman :
{
WriteByte ( 0x10 );
WriteByte (( Byte )((( const cEnderman & ) a_Mob ). GetCarriedBlock ()));
WriteByte ( 0x11 );
WriteByte (( Byte )((( const cEnderman & ) a_Mob ). GetCarriedMeta ()));
WriteByte ( 0x12 );
WriteByte ((( const cEnderman & ) a_Mob ). IsScreaming () ? 1 : 0 );
break ;
}
case cMonster :: mtSkeleton :
{
WriteByte ( 0x0d );
WriteByte ((( const cSkeleton & ) a_Mob ). IsWither () ? 1 : 0 );
break ;
}
case cMonster :: mtWitch :
{
WriteByte ( 0x15 );
WriteByte ((( const cWitch & ) a_Mob ). IsAngry () ? 1 : 0 );
break ;
}
case cMonster :: mtSlime :
{
WriteByte ( 0x10 );
WriteByte ((( const cSlime & ) a_Mob ). GetSize ());
break ;
}
case cMonster :: mtMagmaCube :
{
WriteByte ( 0x10 );
WriteByte ((( const cMagmaCube & ) a_Mob ). GetSize ());
break ;
}
case cMonster :: mtHorse :
{
const cHorse & Horse = ( const cHorse & ) a_Mob ;
int Flags = 0 ;
if ( Horse . IsTame ())
{
Flags |= 0x02 ;
}
if ( Horse . IsSaddled ())
{
Flags |= 0x04 ;
}
if ( Horse . IsChested ())
{
Flags |= 0x08 ;
}
if ( Horse . IsBaby ())
{
Flags |= 0x10 ;
}
if ( Horse . IsEating ())
{
Flags |= 0x20 ;
}
if ( Horse . IsRearing ())
{
Flags |= 0x40 ;
}
if ( Horse . IsMthOpen ())
{
Flags |= 0x80 ;
}
WriteByte ( 0x50 ); // Int at index 16
WriteInt ( Flags );
WriteByte ( 0x13 ); // Byte at index 19
WriteByte ( Horse . GetHorseType ());
WriteByte ( 0x54 ); // Int at index 20
int Appearance = 0 ;
Appearance = Horse . GetHorseColor ();
Appearance |= Horse . GetHorseStyle () << 8 ;
WriteInt ( Appearance );
WriteByte ( 0x56 ); // Int at index 22
WriteInt ( Horse . GetHorseArmour ());
break ;
}
} // switch (a_Mob.GetType())
}
void cProtocol172 :: cPacketizer :: WriteEntityProperties ( const cEntity & a_Entity )
{
if ( ! a_Entity . IsMob ())
{
// No properties for anything else than mobs
WriteInt ( 0 );
return ;
}
2014-02-04 23:07:22 +00:00
//const cMonster & Mob = (const cMonster &)a_Entity;
2013-11-08 20:56:19 +01:00
// TODO: Send properties and modifiers based on the mob type
WriteInt ( 0 ); // NumProperties
2013-11-04 21:20:36 +01:00
}