2014-09-04 00:29:36 +02:00
// Protocol18x.cpp
/*
Implements the 1.8.x protocol classes:
- cProtocol180
- release 1.8.0 protocol (#47)
(others may be added later in the future for the 1.8 release series)
*/
#include "Globals.h"
#include "json/json.h"
#include "Protocol18x.h"
2014-09-08 19:24:33 +02:00
#include "ChunkDataSerializer.h"
#include "PolarSSL++/Sha1Checksum.h"
2015-03-22 19:46:08 +01:00
#include "Packetizer.h"
2014-09-04 00:29:36 +02:00
#include "../ClientHandle.h"
#include "../Root.h"
#include "../Server.h"
#include "../World.h"
2014-09-08 19:24:33 +02:00
#include "../StringCompression.h"
#include "../CompositeChat.h"
#include "../Statistics.h"
2014-09-04 00:29:36 +02:00
2014-09-08 19:24:33 +02:00
#include "../WorldStorage/FastNBT.h"
#include "../WorldStorage/EnchantmentSerializer.h"
#include "../Entities/ExpOrb.h"
#include "../Entities/Minecart.h"
#include "../Entities/FallingBlock.h"
2014-09-08 00:36:30 +02:00
#include "../Entities/Painting.h"
2014-09-08 19:24:33 +02:00
#include "../Entities/Pickup.h"
#include "../Entities/Player.h"
#include "../Entities/ItemFrame.h"
#include "../Entities/ArrowEntity.h"
#include "../Entities/FireworkEntity.h"
#include "../Mobs/IncludeAllMonsters.h"
#include "../UI/Window.h"
#include "../BlockEntities/BeaconEntity.h"
#include "../BlockEntities/CommandBlockEntity.h"
#include "../BlockEntities/MobHeadEntity.h"
2014-11-18 15:33:41 +01:00
#include "../BlockEntities/MobSpawnerEntity.h"
2014-09-08 19:24:33 +02:00
#include "../BlockEntities/FlowerPotEntity.h"
#include "Bindings/PluginManager.h"
2014-09-04 00:29:36 +02:00
2014-09-04 03:22:35 +02:00
2015-03-21 13:00:20 +01:00
/** The slot number that the client uses to indicate "outside the window". */
2015-05-16 23:22:50 +02:00
static const Int16 SLOT_NUM_OUTSIDE = - 999 ;
2015-03-21 13:00:20 +01:00
2014-09-04 03:22:35 +02:00
#define HANDLE_READ(ByteBuf, Proc, Type, Var) \
Type Var; \
if (!ByteBuf.Proc(Var))\
{\
return;\
}
2014-09-04 00:29:36 +02:00
2014-09-08 17:02:54 +02:00
#define HANDLE_PACKET_READ(ByteBuf, Proc, Type, Var) \
Type Var; \
{ \
if (!ByteBuf.Proc(Var)) \
{ \
ByteBuf.CheckValid(); \
return false; \
} \
ByteBuf.CheckValid(); \
}
2014-09-04 00:29:36 +02:00
const int MAX_ENC_LEN = 512 ; // Maximum size of the encrypted message; should be 128, but who knows...
2014-09-19 15:07:01 +02:00
const uLongf MAX_COMPRESSED_PACKET_LEN = 200 KiB ; // Maximum size of compressed packets.
2014-09-04 00:29:36 +02:00
2014-09-08 19:24:33 +02:00
// fwd: main.cpp:
extern bool g_ShouldLogCommIn , g_ShouldLogCommOut ;
2014-09-04 00:29:36 +02:00
2014-09-08 19:24:33 +02:00
////////////////////////////////////////////////////////////////////////////////
// cProtocol180:
2014-09-04 00:29:36 +02:00
2014-09-08 19:24:33 +02:00
cProtocol180 :: cProtocol180 ( cClientHandle * a_Client , const AString & a_ServerAddress , UInt16 a_ServerPort , UInt32 a_State ) :
2014-09-11 22:27:35 +02:00
super ( a_Client ),
2014-09-08 19:24:33 +02:00
m_ServerAddress ( a_ServerAddress ),
m_ServerPort ( a_ServerPort ),
m_State ( a_State ),
m_ReceivedData ( 32 KiB ),
m_IsEncrypted ( false ),
m_LastSentDimension ( dimNotSet )
2014-09-08 17:02:54 +02:00
{
2015-09-06 09:58:18 -04:00
// BungeeCord handling:
// If BC is setup with ip_forward == true, it sends additional data in the login packet's ServerAddress field:
// hostname\00ip-address\00uuid\00profile-properties-as-json
AStringVector Params ;
if ( cRoot :: Get () -> GetServer () -> ShouldAllowBungeeCord () && SplitZeroTerminatedStrings ( a_ServerAddress , Params ) && ( Params . size () == 4 ))
{
LOGD ( "Player at %s connected via BungeeCord" , Params [ 1 ]. c_str ());
m_ServerAddress = Params [ 0 ];
m_Client -> SetIPString ( Params [ 1 ]);
m_Client -> SetUUID ( cMojangAPI :: MakeUUIDShort ( Params [ 2 ]));
m_Client -> SetProperties ( Params [ 3 ]);
}
2014-09-08 19:24:33 +02:00
// Create the comm log file, if so requested:
if ( g_ShouldLogCommIn || g_ShouldLogCommOut )
2014-09-08 17:02:54 +02:00
{
2014-09-08 19:24:33 +02:00
static int sCounter = 0 ;
cFile :: CreateFolder ( "CommLogs" );
2015-01-25 17:06:15 +01:00
AString IP ( a_Client -> GetIPString ());
ReplaceString ( IP , ":" , "_" );
2015-01-24 15:59:41 +01:00
AString FileName = Printf ( "CommLogs/%x_%d__%s.log" ,
static_cast < unsigned > ( time ( nullptr )),
sCounter ++ ,
2015-01-25 17:06:15 +01:00
IP . c_str ()
2015-01-24 15:59:41 +01:00
);
if ( ! m_CommLogFile . Open ( FileName , cFile :: fmWrite ))
{
LOG ( "Cannot log communication to file, the log file \" %s \" cannot be opened for writing." , FileName . c_str ());
}
2014-09-08 17:02:54 +02:00
}
}
2014-09-08 19:24:33 +02:00
void cProtocol180 :: DataReceived ( const char * a_Data , size_t a_Size )
2014-09-08 17:02:54 +02:00
{
2014-09-08 19:24:33 +02:00
if ( m_IsEncrypted )
2014-09-08 17:02:54 +02:00
{
2014-09-08 19:24:33 +02:00
Byte Decrypted [ 512 ];
while ( a_Size > 0 )
{
size_t NumBytes = ( a_Size > sizeof ( Decrypted )) ? sizeof ( Decrypted ) : a_Size ;
2015-07-29 09:04:03 -06:00
m_Decryptor . ProcessData ( Decrypted , reinterpret_cast < const Byte *> ( a_Data ), NumBytes );
AddReceivedData ( reinterpret_cast < const char *> ( Decrypted ), NumBytes );
2014-09-08 19:24:33 +02:00
a_Size -= NumBytes ;
a_Data += NumBytes ;
}
2014-09-08 17:02:54 +02:00
}
else
{
2014-09-08 19:24:33 +02:00
AddReceivedData ( a_Data , a_Size );
2014-09-08 17:02:54 +02:00
}
}
2014-09-08 19:24:33 +02:00
void cProtocol180 :: SendAttachEntity ( const cEntity & a_Entity , const cEntity * a_Vehicle )
2014-09-08 00:36:30 +02:00
{
ASSERT ( m_State == 3 ); // In game mode?
2014-09-08 19:24:33 +02:00
cPacketizer Pkt ( * this , 0x1b ); // Attach Entity packet
2015-03-22 23:09:23 +01:00
Pkt . WriteBEUInt32 ( a_Entity . GetUniqueID ());
Pkt . WriteBEUInt32 (( a_Vehicle != nullptr ) ? a_Vehicle -> GetUniqueID () : 0 );
2014-09-08 00:36:30 +02:00
Pkt . WriteBool ( false );
}
2014-09-08 19:24:33 +02:00
void cProtocol180 :: SendBlockAction ( int a_BlockX , int a_BlockY , int a_BlockZ , char a_Byte1 , char a_Byte2 , BLOCKTYPE a_BlockType )
2014-09-04 19:03:21 +02:00
{
2014-09-08 19:24:33 +02:00
ASSERT ( m_State == 3 ); // In game mode?
cPacketizer Pkt ( * this , 0x24 ); // Block Action packet
2015-03-22 23:09:23 +01:00
Pkt . WritePosition64 ( a_BlockX , a_BlockY , a_BlockZ );
Pkt . WriteBEInt8 ( a_Byte1 );
Pkt . WriteBEInt8 ( a_Byte2 );
Pkt . WriteVarInt32 ( a_BlockType );
2014-09-04 19:03:21 +02:00
}
2015-03-21 16:11:57 +01:00
void cProtocol180 :: SendBlockBreakAnim ( UInt32 a_EntityID , int a_BlockX , int a_BlockY , int a_BlockZ , char a_Stage )
2014-09-04 03:22:35 +02:00
{
ASSERT ( m_State == 3 ); // In game mode?
2014-09-08 19:24:33 +02:00
cPacketizer Pkt ( * this , 0x25 ); // Block Break Animation packet
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( a_EntityID );
Pkt . WritePosition64 ( a_BlockX , a_BlockY , a_BlockZ );
Pkt . WriteBEInt8 ( a_Stage );
2014-09-04 03:22:35 +02:00
}
2014-09-08 19:24:33 +02:00
void cProtocol180 :: SendBlockChange ( int a_BlockX , int a_BlockY , int a_BlockZ , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta )
2014-09-04 03:22:35 +02:00
{
ASSERT ( m_State == 3 ); // In game mode?
2014-09-08 19:24:33 +02:00
cPacketizer Pkt ( * this , 0x23 ); // Block Change packet
2015-03-22 23:09:23 +01:00
Pkt . WritePosition64 ( a_BlockX , a_BlockY , a_BlockZ );
2015-07-29 09:04:03 -06:00
Pkt . WriteVarInt32 (( static_cast < UInt32 > ( a_BlockType ) << 4 ) | ( static_cast < UInt32 > ( a_BlockMeta ) & 15 ));
2014-09-04 03:22:35 +02:00
}
2014-09-08 19:24:33 +02:00
void cProtocol180 :: SendBlockChanges ( int a_ChunkX , int a_ChunkZ , const sSetBlockVector & a_Changes )
2014-09-04 03:22:35 +02:00
{
ASSERT ( m_State == 3 ); // In game mode?
2014-09-08 19:24:33 +02:00
2014-09-09 18:27:31 +02:00
cPacketizer Pkt ( * this , 0x22 ); // Multi Block Change packet
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt32 ( a_ChunkX );
Pkt . WriteBEInt32 ( a_ChunkZ );
2015-07-29 09:04:03 -06:00
Pkt . WriteVarInt32 ( static_cast < UInt32 > ( a_Changes . size ()));
2014-09-08 19:24:33 +02:00
for ( sSetBlockVector :: const_iterator itr = a_Changes . begin (), end = a_Changes . end (); itr != end ; ++ itr )
{
2015-03-22 23:09:23 +01:00
Int16 Coords = static_cast < Int16 > ( itr -> m_RelY | ( itr -> m_RelZ << 8 ) | ( itr -> m_RelX << 12 ));
Pkt . WriteBEInt16 ( Coords );
2015-07-29 09:04:03 -06:00
Pkt . WriteVarInt32 ( static_cast < UInt32 > ( itr -> m_BlockType & 0xFFF ) << 4 | ( itr -> m_BlockMeta & 0xF ));
2014-09-09 18:27:31 +02:00
} // for itr - a_Changes[]
2014-09-04 03:22:35 +02:00
}
2015-09-20 23:07:53 +01:00
void cProtocol180 :: SendChat ( const AString & a_Message , eChatType a_Type )
2014-09-04 03:22:35 +02:00
{
ASSERT ( m_State == 3 ); // In game mode?
2014-09-08 19:24:33 +02:00
cPacketizer Pkt ( * this , 0x02 ); // Chat Message packet
Pkt . WriteString ( Printf ( "{ \" text \" : \" %s \" }" , EscapeString ( a_Message ). c_str ()));
2015-09-20 23:07:53 +01:00
Pkt . WriteBEInt8 ( a_Type );
2014-09-04 03:22:35 +02:00
}
2015-09-20 23:07:53 +01:00
void cProtocol180 :: SendChat ( const cCompositeChat & a_Message , eChatType a_Type , bool a_ShouldUseChatPrefixes )
2014-09-04 03:22:35 +02:00
{
ASSERT ( m_State == 3 ); // In game mode?
2014-09-13 20:27:10 +02:00
2014-09-08 19:24:33 +02:00
// Send the message to the client:
cPacketizer Pkt ( * this , 0x02 );
2015-09-20 23:07:53 +01:00
Pkt . WriteString ( a_Message . CreateJsonString ( a_ShouldUseChatPrefixes ));
Pkt . WriteBEInt8 ( a_Type );
2014-09-04 03:22:35 +02:00
}
2014-09-08 19:24:33 +02:00
void cProtocol180 :: SendChunkData ( int a_ChunkX , int a_ChunkZ , cChunkDataSerializer & a_Serializer )
2014-09-04 03:22:35 +02:00
{
ASSERT ( m_State == 3 ); // In game mode?
2014-09-08 19:24:33 +02: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_8_0 , a_ChunkX , a_ChunkZ );
cCSLock Lock ( m_CSPacket );
SendData ( ChunkData . data (), ChunkData . size ());
2014-09-04 03:22:35 +02:00
}
2014-09-08 19:24:33 +02:00
void cProtocol180 :: SendCollectEntity ( const cEntity & a_Entity , const cPlayer & a_Player )
2014-09-04 03:22:35 +02:00
{
ASSERT ( m_State == 3 ); // In game mode?
2014-09-08 19:24:33 +02:00
cPacketizer Pkt ( * this , 0x0d ); // Collect Item packet
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( a_Entity . GetUniqueID ());
Pkt . WriteVarInt32 ( a_Player . GetUniqueID ());
2014-09-04 03:22:35 +02:00
}
2014-09-08 19:24:33 +02:00
void cProtocol180 :: SendDestroyEntity ( const cEntity & a_Entity )
2014-09-04 03:22:35 +02:00
{
ASSERT ( m_State == 3 ); // In game mode?
2014-09-09 18:27:31 +02:00
cPacketizer Pkt ( * this , 0x13 ); // Destroy Entities packet
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( 1 );
Pkt . WriteVarInt32 ( a_Entity . GetUniqueID ());
2014-09-04 03:22:35 +02:00
}
2014-09-08 19:24:33 +02:00
void cProtocol180 :: SendDisconnect ( const AString & a_Reason )
{
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 ;
}
}
}
void cProtocol180 :: SendEditSign ( int a_BlockX , int a_BlockY , int a_BlockZ )
2014-09-04 03:22:35 +02:00
{
ASSERT ( m_State == 3 ); // In game mode?
2014-09-08 19:24:33 +02:00
cPacketizer Pkt ( * this , 0x36 ); // Sign Editor Open packet
2015-03-22 23:09:23 +01:00
Pkt . WritePosition64 ( a_BlockX , a_BlockY , a_BlockZ );
2014-09-08 19:24:33 +02:00
}
void cProtocol180 :: SendEntityEffect ( const cEntity & a_Entity , int a_EffectID , int a_Amplifier , short a_Duration )
{
ASSERT ( m_State == 3 ); // In game mode?
cPacketizer Pkt ( * this , 0x1D ); // Entity Effect packet
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( a_Entity . GetUniqueID ());
Pkt . WriteBEUInt8 ( static_cast < UInt8 > ( a_EffectID ));
Pkt . WriteBEUInt8 ( static_cast < UInt8 > ( a_Amplifier ));
2015-07-29 09:04:03 -06:00
Pkt . WriteVarInt32 ( static_cast < UInt32 > ( a_Duration ));
2014-09-08 19:24:33 +02:00
Pkt . WriteBool ( false ); // Hide particles
2014-09-04 03:22:35 +02:00
}
2014-09-08 19:24:33 +02:00
void cProtocol180 :: SendEntityEquipment ( const cEntity & a_Entity , short a_SlotNum , const cItem & a_Item )
2014-09-04 03:22:35 +02:00
{
ASSERT ( m_State == 3 ); // In game mode?
2014-09-09 18:27:31 +02:00
cPacketizer Pkt ( * this , 0x04 ); // Entity Equipment packet
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( a_Entity . GetUniqueID ());
Pkt . WriteBEInt16 ( a_SlotNum );
2015-03-22 19:46:08 +01:00
WriteItem ( Pkt , a_Item );
2014-09-04 03:22:35 +02:00
}
2014-09-08 19:24:33 +02:00
void cProtocol180 :: SendEntityHeadLook ( const cEntity & a_Entity )
2014-09-04 03:22:35 +02:00
{
ASSERT ( m_State == 3 ); // In game mode?
2014-09-09 18:27:31 +02:00
cPacketizer Pkt ( * this , 0x19 ); // Entity Head Look packet
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( a_Entity . GetUniqueID ());
2014-09-09 18:27:31 +02:00
Pkt . WriteByteAngle ( a_Entity . GetHeadYaw ());
2014-09-04 03:22:35 +02:00
}
2014-09-08 19:24:33 +02:00
void cProtocol180 :: SendEntityLook ( const cEntity & a_Entity )
2014-09-04 03:22:35 +02:00
{
ASSERT ( m_State == 3 ); // In game mode?
2014-09-09 18:27:31 +02:00
cPacketizer Pkt ( * this , 0x16 ); // Entity Look packet
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( a_Entity . GetUniqueID ());
2014-09-08 19:24:33 +02:00
Pkt . WriteByteAngle ( a_Entity . GetYaw ());
Pkt . WriteByteAngle ( a_Entity . GetPitch ());
2015-02-06 21:40:20 +01:00
Pkt . WriteBool ( a_Entity . IsOnGround ());
2014-09-04 03:22:35 +02:00
}
2014-09-08 19:24:33 +02:00
void cProtocol180 :: SendEntityMetadata ( const cEntity & a_Entity )
2014-09-04 03:22:35 +02:00
{
ASSERT ( m_State == 3 ); // In game mode?
2014-09-09 18:27:31 +02:00
cPacketizer Pkt ( * this , 0x1c ); // Entity Metadata packet
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( a_Entity . GetUniqueID ());
2015-03-22 19:46:08 +01:00
WriteEntityMetadata ( Pkt , a_Entity );
2015-03-22 23:09:23 +01:00
Pkt . WriteBEUInt8 ( 0x7f ); // The termination byte
2014-09-04 03:22:35 +02:00
}
void cProtocol180 :: SendEntityProperties ( const cEntity & a_Entity )
{
ASSERT ( m_State == 3 ); // In game mode?
cPacketizer Pkt ( * this , 0x20 ); // Entity Properties packet
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( a_Entity . GetUniqueID ());
2015-03-22 19:46:08 +01:00
WriteEntityProperties ( Pkt , a_Entity );
2014-09-04 03:22:35 +02:00
}
2014-09-08 19:24:33 +02:00
void cProtocol180 :: SendEntityRelMove ( const cEntity & a_Entity , char a_RelX , char a_RelY , char a_RelZ )
2014-09-04 03:22:35 +02:00
{
2014-09-08 19:24:33 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2014-09-04 03:22:35 +02:00
2014-09-09 18:27:31 +02:00
cPacketizer Pkt ( * this , 0x15 ); // Entity Relative Move packet
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( a_Entity . GetUniqueID ());
Pkt . WriteBEInt8 ( a_RelX );
Pkt . WriteBEInt8 ( a_RelY );
Pkt . WriteBEInt8 ( a_RelZ );
2015-02-06 21:40:20 +01:00
Pkt . WriteBool ( a_Entity . IsOnGround ());
2014-09-04 03:22:35 +02:00
}
2014-09-08 19:24:33 +02:00
void cProtocol180 :: SendEntityRelMoveLook ( const cEntity & a_Entity , char a_RelX , char a_RelY , char a_RelZ )
2014-09-04 03:22:35 +02:00
{
ASSERT ( m_State == 3 ); // In game mode?
2014-09-08 19:24:33 +02:00
2014-09-09 18:27:31 +02:00
cPacketizer Pkt ( * this , 0x17 ); // Entity Look And Relative Move packet
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( a_Entity . GetUniqueID ());
Pkt . WriteBEInt8 ( a_RelX );
Pkt . WriteBEInt8 ( a_RelY );
Pkt . WriteBEInt8 ( a_RelZ );
2014-09-08 19:24:33 +02:00
Pkt . WriteByteAngle ( a_Entity . GetYaw ());
Pkt . WriteByteAngle ( a_Entity . GetPitch ());
2015-02-06 21:40:20 +01:00
Pkt . WriteBool ( a_Entity . IsOnGround ());
2014-09-04 03:22:35 +02:00
}
2014-09-08 19:24:33 +02:00
void cProtocol180 :: SendEntityStatus ( const cEntity & a_Entity , char a_Status )
2014-09-04 03:22:35 +02:00
{
ASSERT ( m_State == 3 ); // In game mode?
2014-09-08 19:24:33 +02:00
cPacketizer Pkt ( * this , 0x1a ); // Entity Status packet
2015-03-22 23:09:23 +01:00
Pkt . WriteBEUInt32 ( a_Entity . GetUniqueID ());
Pkt . WriteBEInt8 ( a_Status );
2014-09-04 03:22:35 +02:00
}
2014-09-08 19:24:33 +02:00
void cProtocol180 :: SendEntityVelocity ( const cEntity & a_Entity )
2014-09-04 03:22:35 +02:00
{
ASSERT ( m_State == 3 ); // In game mode?
2014-09-08 19:24:33 +02:00
cPacketizer Pkt ( * this , 0x12 ); // Entity Velocity packet
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( a_Entity . GetUniqueID ());
2015-05-09 09:25:09 +02:00
// 400 = 8000 / 20 ... Conversion from our speed in m / s to 8000 m / tick
2015-07-29 09:04:03 -06:00
Pkt . WriteBEInt16 ( static_cast < Int16 > ( a_Entity . GetSpeedX () * 400 ));
Pkt . WriteBEInt16 ( static_cast < Int16 > ( a_Entity . GetSpeedY () * 400 ));
Pkt . WriteBEInt16 ( static_cast < Int16 > ( a_Entity . GetSpeedZ () * 400 ));
2014-09-04 03:22:35 +02:00
}
2014-09-08 19:24:33 +02:00
void cProtocol180 :: SendExplosion ( double a_BlockX , double a_BlockY , double a_BlockZ , float a_Radius , const cVector3iArray & a_BlocksAffected , const Vector3d & a_PlayerMotion )
2014-09-04 03:22:35 +02:00
{
ASSERT ( m_State == 3 ); // In game mode?
2014-09-08 19:24:33 +02:00
cPacketizer Pkt ( * this , 0x27 ); // Explosion packet
2015-03-22 23:09:23 +01:00
Pkt . WriteBEFloat ( static_cast < float > ( a_BlockX ));
Pkt . WriteBEFloat ( static_cast < float > ( a_BlockY ));
Pkt . WriteBEFloat ( static_cast < float > ( a_BlockZ ));
Pkt . WriteBEFloat ( static_cast < float > ( a_Radius ));
Pkt . WriteBEUInt32 ( static_cast < UInt32 > ( a_BlocksAffected . size ()));
2014-09-08 19:24:33 +02:00
for ( cVector3iArray :: const_iterator itr = a_BlocksAffected . begin (), end = a_BlocksAffected . end (); itr != end ; ++ itr )
{
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt8 ( static_cast < Int8 > ( itr -> x ));
Pkt . WriteBEInt8 ( static_cast < Int8 > ( itr -> y ));
Pkt . WriteBEInt8 ( static_cast < Int8 > ( itr -> z ));
2014-09-08 19:24:33 +02:00
} // for itr - a_BlockAffected[]
2015-03-22 23:09:23 +01:00
Pkt . WriteBEFloat ( static_cast < float > ( a_PlayerMotion . x ));
Pkt . WriteBEFloat ( static_cast < float > ( a_PlayerMotion . y ));
Pkt . WriteBEFloat ( static_cast < float > ( a_PlayerMotion . z ));
2014-09-04 03:22:35 +02:00
}
2014-09-08 19:24:33 +02:00
void cProtocol180 :: SendGameMode ( eGameMode a_GameMode )
2014-09-04 03:22:35 +02:00
{
ASSERT ( m_State == 3 ); // In game mode?
2014-09-08 19:24:33 +02:00
cPacketizer Pkt ( * this , 0x2b ); // Change Game State packet
2015-03-22 23:09:23 +01:00
Pkt . WriteBEUInt8 ( 3 ); // Reason: Change game mode
Pkt . WriteBEFloat ( static_cast < float > ( a_GameMode )); // The protocol really represents the value with a float!
2014-09-04 03:22:35 +02:00
}
2014-09-08 19:24:33 +02:00
void cProtocol180 :: SendHealth ( void )
2014-09-04 03:22:35 +02:00
{
ASSERT ( m_State == 3 ); // In game mode?
2014-09-08 19:24:33 +02:00
cPacketizer Pkt ( * this , 0x06 ); // Update Health packet
cPlayer * Player = m_Client -> GetPlayer ();
2015-03-22 23:09:23 +01:00
Pkt . WriteBEFloat ( static_cast < float > ( Player -> GetHealth ()));
2015-07-29 09:04:03 -06:00
Pkt . WriteVarInt32 ( static_cast < UInt32 > ( Player -> GetFoodLevel ()));
2015-03-22 23:09:23 +01:00
Pkt . WriteBEFloat ( static_cast < float > ( Player -> GetFoodSaturationLevel ()));
2014-09-04 03:22:35 +02:00
}
2015-04-08 00:41:19 +10:00
void cProtocol180 :: SendHideTitle ( void )
{
ASSERT ( m_State == 3 ); // In game mode?
cPacketizer Pkt ( * this , 0x45 ); // Title packet
Pkt . WriteVarInt32 ( 3 ); // Hide title
}
2014-09-08 19:24:33 +02:00
void cProtocol180 :: SendInventorySlot ( char a_WindowID , short a_SlotNum , const cItem & a_Item )
2014-09-04 00:29:36 +02:00
{
ASSERT ( m_State == 3 ); // In game mode?
2014-09-08 19:24:33 +02:00
cPacketizer Pkt ( * this , 0x2f ); // Set Slot packet
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt8 ( a_WindowID );
Pkt . WriteBEInt16 ( a_SlotNum );
2015-03-22 19:46:08 +01:00
WriteItem ( Pkt , a_Item );
2014-09-04 00:29:36 +02:00
}
2015-07-29 09:04:03 -06:00
void cProtocol180 :: SendKeepAlive ( UInt32 a_PingID )
2014-09-04 00:29:36 +02:00
{
2014-09-08 19:24:33 +02:00
// Drop the packet if the protocol is not in the Game state yet (caused a client crash):
if ( m_State != 3 )
2014-09-04 00:29:36 +02:00
{
2014-09-08 19:24:33 +02:00
LOGWARNING ( "Trying to send a KeepAlive packet to a player who's not yet fully logged in (%d). The protocol class prevented the packet." , m_State );
return ;
}
cPacketizer Pkt ( * this , 0x00 ); // Keep Alive packet
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( a_PingID );
2014-09-04 00:29:36 +02:00
}
2014-09-08 19:24:33 +02:00
void cProtocol180 :: SendLogin ( const cPlayer & a_Player , const cWorld & a_World )
2014-09-04 00:29:36 +02:00
{
2014-09-08 19:24:33 +02:00
// Send the Join Game packet:
{
cServer * Server = cRoot :: Get () -> GetServer ();
cPacketizer Pkt ( * this , 0x01 ); // Join Game packet
2015-03-22 23:09:23 +01:00
Pkt . WriteBEUInt32 ( a_Player . GetUniqueID ());
2015-07-29 09:04:03 -06:00
Pkt . WriteBEUInt8 ( static_cast < UInt8 > ( a_Player . GetEffectiveGameMode ()) | ( Server -> IsHardcore () ? 0x08 : 0 )); // Hardcore flag bit 4
Pkt . WriteBEInt8 ( static_cast < Int8 > ( a_World . GetDimension ()));
2015-03-22 23:09:23 +01:00
Pkt . WriteBEUInt8 ( 2 ); // TODO: Difficulty (set to Normal)
2015-08-05 01:24:59 +03:00
Pkt . WriteBEUInt8 ( static_cast < UInt8 > ( Clamp < int > ( Server -> GetMaxPlayers (), 0 , 255 )));
2014-09-08 19:24:33 +02:00
Pkt . WriteString ( "default" ); // Level type - wtf?
Pkt . WriteBool ( false ); // Reduced Debug Info - wtf?
}
m_LastSentDimension = a_World . GetDimension ();
2014-09-04 00:29:36 +02:00
2014-09-08 19:24:33 +02:00
// Send the spawn position:
{
cPacketizer Pkt ( * this , 0x05 ); // Spawn Position packet
2015-07-29 09:04:03 -06:00
Pkt . WritePosition64 ( FloorC ( a_World . GetSpawnX ()), FloorC ( a_World . GetSpawnY ()), FloorC ( a_World . GetSpawnZ ()));
2014-09-08 19:24:33 +02:00
}
2014-09-12 02:00:28 +02:00
// Send the server difficulty:
{
cPacketizer Pkt ( * this , 0x41 );
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt8 ( 1 );
2014-09-12 02:00:28 +02:00
}
2014-09-08 19:24:33 +02:00
// Send player abilities:
SendPlayerAbilities ();
2014-09-04 00:29:36 +02:00
}
2014-09-08 19:24:33 +02:00
void cProtocol180 :: SendLoginSuccess ( void )
2014-09-04 00:29:36 +02:00
{
2014-09-08 19:24:33 +02:00
ASSERT ( m_State == 2 ); // State: login?
2014-09-04 19:03:21 +02:00
2014-09-08 19:24:33 +02:00
// Enable compression:
2014-09-04 00:29:36 +02:00
{
2014-09-08 19:24:33 +02:00
cPacketizer Pkt ( * this , 0x03 ); // Set compression packet
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( 256 );
2014-09-08 19:24:33 +02:00
}
m_State = 3 ; // State = Game
{
cPacketizer Pkt ( * this , 0x02 ); // Login success packet
Pkt . WriteString ( cMojangAPI :: MakeUUIDDashed ( m_Client -> GetUUID ()));
Pkt . WriteString ( m_Client -> GetUsername ());
}
}
void cProtocol180 :: SendPaintingSpawn ( const cPainting & a_Painting )
{
ASSERT ( m_State == 3 ); // In game mode?
2014-09-11 20:06:28 +02:00
double PosX = a_Painting . GetPosX ();
double PosY = a_Painting . GetPosY ();
double PosZ = a_Painting . GetPosZ ();
cPacketizer Pkt ( * this , 0x10 ); // Spawn Painting packet
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( a_Painting . GetUniqueID ());
2014-09-08 19:24:33 +02:00
Pkt . WriteString ( a_Painting . GetName (). c_str ());
2015-07-29 09:04:03 -06:00
Pkt . WritePosition64 ( static_cast < Int32 > ( PosX ), static_cast < Int32 > ( PosY ), static_cast < Int32 > ( PosZ ));
Pkt . WriteBEInt8 ( static_cast < Int8 > ( a_Painting . GetProtocolFacing ()));
2014-09-08 19:24:33 +02:00
}
2015-06-30 15:50:15 +01:00
void cProtocol180 :: SendMapData ( const cMap & a_Map , int a_DataStartX , int a_DataStartY )
2014-09-08 19:24:33 +02:00
{
ASSERT ( m_State == 3 ); // In game mode?
cPacketizer Pkt ( * this , 0x34 );
2015-06-30 15:50:15 +01:00
Pkt . WriteVarInt32 ( a_Map . GetID ());
2015-07-29 09:04:03 -06:00
Pkt . WriteBEUInt8 ( static_cast < UInt8 > ( a_Map . GetScale ()));
2014-09-08 19:24:33 +02:00
2015-06-30 15:50:15 +01:00
Pkt . WriteVarInt32 ( static_cast < UInt32 > ( a_Map . GetDecorators (). size ()));
2015-07-21 23:40:13 +01:00
for ( const auto & Decorator : a_Map . GetDecorators ())
2014-09-08 19:24:33 +02:00
{
2015-07-29 09:04:03 -06:00
Pkt . WriteBEUInt8 ( static_cast < Byte > (( static_cast < Int32 > ( Decorator . GetType ()) << 4 ) | ( Decorator . GetRot () & 0xF )));
Pkt . WriteBEUInt8 ( static_cast < UInt8 > ( Decorator . GetPixelX ()));
Pkt . WriteBEUInt8 ( static_cast < UInt8 > ( Decorator . GetPixelZ ()));
2014-09-08 19:24:33 +02:00
}
2015-06-30 15:50:15 +01:00
Pkt . WriteBEUInt8 ( 128 );
Pkt . WriteBEUInt8 ( 128 );
2015-07-29 09:04:03 -06:00
Pkt . WriteBEUInt8 ( static_cast < UInt8 > ( a_DataStartX ));
Pkt . WriteBEUInt8 ( static_cast < UInt8 > ( a_DataStartY ));
2015-06-30 15:50:15 +01:00
Pkt . WriteVarInt32 ( static_cast < UInt32 > ( a_Map . GetData (). size ()));
for ( auto itr = a_Map . GetData (). cbegin (); itr != a_Map . GetData (). cend (); ++ itr )
2014-09-08 19:24:33 +02:00
{
2015-06-30 15:50:15 +01:00
Pkt . WriteBEUInt8 ( * itr );
2014-09-08 19:24:33 +02:00
}
}
void cProtocol180 :: SendPickupSpawn ( const cPickup & a_Pickup )
{
ASSERT ( m_State == 3 ); // In game mode?
2014-09-11 17:03:09 +02:00
{
2014-09-08 19:24:33 +02:00
cPacketizer Pkt ( * this , 0x0e ); // Spawn Object packet
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( a_Pickup . GetUniqueID ());
Pkt . WriteBEUInt8 ( 2 ); // Type = Pickup
2014-09-08 19:24:33 +02:00
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 ());
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt32 ( 0 ); // No object data
2014-09-08 19:24:33 +02:00
}
{
cPacketizer Pkt ( * this , 0x1c ); // Entity Metadata packet
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( a_Pickup . GetUniqueID ());
Pkt . WriteBEUInt8 (( 0x05 << 5 ) | 10 ); // Slot type + index 10
2015-03-22 19:46:08 +01:00
WriteItem ( Pkt , a_Pickup . GetItem ());
2015-03-22 23:09:23 +01:00
Pkt . WriteBEUInt8 ( 0x7f ); // End of metadata
2014-09-11 17:03:09 +02:00
}
2014-09-08 19:24:33 +02:00
}
void cProtocol180 :: SendPlayerAbilities ( void )
{
ASSERT ( m_State == 3 ); // In game mode?
cPacketizer Pkt ( * this , 0x39 ); // Player Abilities packet
Byte Flags = 0 ;
cPlayer * Player = m_Client -> GetPlayer ();
if ( Player -> IsGameModeCreative ())
{
Flags |= 0x01 ;
Flags |= 0x08 ; // Godmode, used for creative
}
if ( Player -> IsFlying ())
{
Flags |= 0x02 ;
}
if ( Player -> CanFly ())
{
Flags |= 0x04 ;
}
2015-03-22 23:09:23 +01:00
Pkt . WriteBEUInt8 ( Flags );
Pkt . WriteBEFloat ( static_cast < float > ( 0.05 * Player -> GetFlyingMaxSpeed ()));
Pkt . WriteBEFloat ( static_cast < float > ( 0.1 * Player -> GetNormalMaxSpeed ()));
2014-09-08 19:24:33 +02:00
}
void cProtocol180 :: SendEntityAnimation ( const cEntity & a_Entity , char a_Animation )
{
ASSERT ( m_State == 3 ); // In game mode?
cPacketizer Pkt ( * this , 0x0b ); // Animation packet
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( a_Entity . GetUniqueID ());
Pkt . WriteBEInt8 ( a_Animation );
2014-09-08 19:24:33 +02:00
}
2014-09-11 17:03:09 +02:00
void cProtocol180 :: 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_ParticleAmount )
2014-09-08 19:24:33 +02:00
{
ASSERT ( m_State == 3 ); // In game mode?
2014-09-11 17:03:09 +02:00
int ParticleID = GetParticleID ( a_ParticleName );
cPacketizer Pkt ( * this , 0x2A );
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt32 ( ParticleID );
2014-09-11 17:03:09 +02:00
Pkt . WriteBool ( false );
2015-03-22 23:09:23 +01:00
Pkt . WriteBEFloat ( a_SrcX );
Pkt . WriteBEFloat ( a_SrcY );
Pkt . WriteBEFloat ( a_SrcZ );
Pkt . WriteBEFloat ( a_OffsetX );
Pkt . WriteBEFloat ( a_OffsetY );
Pkt . WriteBEFloat ( a_OffsetZ );
Pkt . WriteBEFloat ( a_ParticleData );
Pkt . WriteBEInt32 ( a_ParticleAmount );
2014-09-08 19:24:33 +02:00
}
2015-03-21 17:17:26 +00:00
void cProtocol180 :: SendParticleEffect ( const AString & a_ParticleName , Vector3f a_Src , Vector3f a_Offset , float a_ParticleData , int a_ParticleAmount , std :: array < int , 2 > a_Data )
{
ASSERT ( m_State == 3 ); // In game mode?
int ParticleID = GetParticleID ( a_ParticleName );
cPacketizer Pkt ( * this , 0x2A );
Pkt . WriteBEInt32 ( ParticleID );
Pkt . WriteBool ( false );
Pkt . WriteBEFloat ( a_Src . x );
Pkt . WriteBEFloat ( a_Src . y );
Pkt . WriteBEFloat ( a_Src . z );
Pkt . WriteBEFloat ( a_Offset . x );
Pkt . WriteBEFloat ( a_Offset . y );
Pkt . WriteBEFloat ( a_Offset . z );
Pkt . WriteBEFloat ( a_ParticleData );
Pkt . WriteBEInt32 ( a_ParticleAmount );
switch ( ParticleID )
{
// iconcrack
case 36 :
{
Pkt . WriteVarInt32 ( static_cast < UInt32 > ( a_Data [ 0 ]));
Pkt . WriteVarInt32 ( static_cast < UInt32 > ( a_Data [ 1 ]));
break ;
}
// blockcrack
// blockdust
case 37 :
case 38 :
{
Pkt . WriteVarInt32 ( static_cast < UInt32 > ( a_Data [ 0 ]));
break ;
}
default :
{
break ;
}
}
}
2014-09-18 18:50:17 +02:00
void cProtocol180 :: SendPlayerListAddPlayer ( const cPlayer & a_Player )
2014-09-08 19:24:33 +02:00
{
ASSERT ( m_State == 3 ); // In game mode?
2014-09-09 03:02:25 +02:00
cPacketizer Pkt ( * this , 0x38 ); // Playerlist Item packet
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( 0 );
Pkt . WriteVarInt32 ( 1 );
2014-09-09 03:02:25 +02:00
Pkt . WriteUUID ( a_Player . GetUUID ());
2014-09-26 17:37:19 +02:00
Pkt . WriteString ( a_Player . GetPlayerListName ());
2014-09-09 03:02:25 +02:00
2014-09-18 18:50:17 +02:00
const Json :: Value & Properties = a_Player . GetClientHandle () -> GetProperties ();
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( Properties . size ());
2015-10-19 16:03:55 +02:00
for ( auto & Node : Properties )
2014-09-09 03:02:25 +02:00
{
2015-10-19 16:03:55 +02:00
Pkt . WriteString ( Node . get ( "name" , "" ). asString ());
Pkt . WriteString ( Node . get ( "value" , "" ). asString ());
AString Signature = Node . get ( "signature" , "" ). asString ();
2014-09-18 18:50:17 +02:00
if ( Signature . empty ())
2014-09-09 03:02:25 +02:00
{
Pkt . WriteBool ( false );
}
2014-09-18 18:50:17 +02:00
else
2014-09-09 03:02:25 +02:00
{
2014-09-18 18:50:17 +02:00
Pkt . WriteBool ( true );
Pkt . WriteString ( Signature );
2014-09-09 03:02:25 +02:00
}
}
2014-09-18 18:50:17 +02:00
2015-07-29 09:04:03 -06:00
Pkt . WriteVarInt32 ( static_cast < UInt32 > ( a_Player . GetGameMode ()));
Pkt . WriteVarInt32 ( static_cast < UInt32 > ( a_Player . GetClientHandle () -> GetPing ()));
2014-09-26 17:37:19 +02:00
Pkt . WriteBool ( false );
2014-09-18 18:50:17 +02:00
}
void cProtocol180 :: SendPlayerListRemovePlayer ( const cPlayer & a_Player )
{
ASSERT ( m_State == 3 ); // In game mode?
cPacketizer Pkt ( * this , 0x38 ); // Playerlist Item packet
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( 4 );
Pkt . WriteVarInt32 ( 1 );
2014-09-18 18:50:17 +02:00
Pkt . WriteUUID ( a_Player . GetUUID ());
}
void cProtocol180 :: SendPlayerListUpdateGameMode ( const cPlayer & a_Player )
{
ASSERT ( m_State == 3 ); // In game mode?
cPacketizer Pkt ( * this , 0x38 ); // Playerlist Item packet
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( 1 );
Pkt . WriteVarInt32 ( 1 );
2014-09-18 18:50:17 +02:00
Pkt . WriteUUID ( a_Player . GetUUID ());
2015-07-29 09:04:03 -06:00
Pkt . WriteVarInt32 ( static_cast < UInt32 > ( a_Player . GetGameMode ()));
2014-09-18 18:50:17 +02:00
}
void cProtocol180 :: SendPlayerListUpdatePing ( const cPlayer & a_Player )
{
ASSERT ( m_State == 3 ); // In game mode?
2015-02-24 10:04:43 +01:00
auto ClientHandle = a_Player . GetClientHandlePtr ();
if ( ClientHandle != nullptr )
{
cPacketizer Pkt ( * this , 0x38 ); // Playerlist Item packet
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( 2 );
Pkt . WriteVarInt32 ( 1 );
2015-02-24 10:04:43 +01:00
Pkt . WriteUUID ( a_Player . GetUUID ());
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( static_cast < UInt32 > ( ClientHandle -> GetPing ()));
2015-02-24 10:04:43 +01:00
}
2014-09-18 18:50:17 +02:00
}
2014-09-26 17:37:19 +02:00
void cProtocol180 :: SendPlayerListUpdateDisplayName ( const cPlayer & a_Player , const AString & a_CustomName )
2014-09-18 18:50:17 +02:00
{
ASSERT ( m_State == 3 ); // In game mode?
cPacketizer Pkt ( * this , 0x38 ); // Playerlist Item packet
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( 3 );
Pkt . WriteVarInt32 ( 1 );
2014-09-18 18:50:17 +02:00
Pkt . WriteUUID ( a_Player . GetUUID ());
2014-09-26 17:37:19 +02:00
if ( a_CustomName . empty ())
2014-09-18 18:50:17 +02:00
{
Pkt . WriteBool ( false );
}
else
{
Pkt . WriteBool ( true );
2014-09-26 17:37:19 +02:00
Pkt . WriteString ( Printf ( "{ \" text \" : \" %s \" }" , a_CustomName . c_str ()));
2014-09-18 18:50:17 +02:00
}
2014-09-08 19:24:33 +02:00
}
void cProtocol180 :: SendPlayerMaxSpeed ( void )
{
ASSERT ( m_State == 3 ); // In game mode?
cPacketizer Pkt ( * this , 0x20 ); // Entity Properties
cPlayer * Player = m_Client -> GetPlayer ();
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( Player -> GetUniqueID ());
Pkt . WriteBEInt32 ( 1 ); // Count
2014-09-08 19:24:33 +02:00
Pkt . WriteString ( "generic.movementSpeed" );
// The default game speed is 0.1, multiply that value by the relative speed:
2015-03-22 23:09:23 +01:00
Pkt . WriteBEDouble ( 0.1 * Player -> GetNormalMaxSpeed ());
2014-09-08 19:24:33 +02:00
if ( Player -> IsSprinting ())
{
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( 1 ); // Modifier count
Pkt . WriteBEUInt64 ( 0x662a6b8dda3e4c1c );
Pkt . WriteBEUInt64 ( 0x881396ea6097278d ); // UUID of the modifier
Pkt . WriteBEDouble ( Player -> GetSprintingMaxSpeed () - Player -> GetNormalMaxSpeed ());
Pkt . WriteBEUInt8 ( 2 );
2014-09-08 19:24:33 +02:00
}
else
{
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( 0 ); // Modifier count
2014-09-08 19:24:33 +02:00
}
}
void cProtocol180 :: SendPlayerMoveLook ( void )
{
ASSERT ( m_State == 3 ); // In game mode?
cPacketizer Pkt ( * this , 0x08 ); // Player Position And Look packet
cPlayer * Player = m_Client -> GetPlayer ();
2015-03-22 23:09:23 +01:00
Pkt . WriteBEDouble ( Player -> GetPosX ());
2015-07-21 21:25:37 +01:00
Pkt . WriteBEDouble ( Player -> GetPosY ());
2015-03-22 23:09:23 +01:00
Pkt . WriteBEDouble ( Player -> GetPosZ ());
Pkt . WriteBEFloat ( static_cast < float > ( Player -> GetYaw ()));
Pkt . WriteBEFloat ( static_cast < float > ( Player -> GetPitch ()));
Pkt . WriteBEUInt8 ( 0 );
2014-09-08 19:24:33 +02:00
}
void cProtocol180 :: SendPlayerPosition ( void )
{
// There is no dedicated packet for this, send the whole thing:
SendPlayerMoveLook ();
}
void cProtocol180 :: SendPlayerSpawn ( const cPlayer & a_Player )
{
// Called to spawn another player for the client
cPacketizer Pkt ( * this , 0x0c ); // Spawn Player packet
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( a_Player . GetUniqueID ());
2014-09-09 01:54:40 +02:00
Pkt . WriteUUID ( cMojangAPI :: MakeUUIDShort ( a_Player . GetUUID ()));
2014-09-08 19:24:33 +02:00
Pkt . WriteFPInt ( a_Player . GetPosX ());
2015-02-06 21:40:20 +01:00
Pkt . WriteFPInt ( a_Player . GetPosY () + 0.001 ); // The "+ 0.001" is there because otherwise the player falls through the block they were standing on.
2014-09-08 19:24:33 +02:00
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 ;
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt16 ( ItemType );
Pkt . WriteBEUInt8 (( 3 << 5 ) | 6 ); // Metadata: float + index 6
Pkt . WriteBEFloat ( static_cast < float > ( a_Player . GetHealth ()));
Pkt . WriteBEUInt8 (( 4 << 5 | ( 2 & 0x1F )) & 0xFF );
2014-09-08 19:24:33 +02:00
Pkt . WriteString ( a_Player . GetName ());
2015-03-22 23:09:23 +01:00
Pkt . WriteBEUInt8 ( 0x7f ); // Metadata: end
2014-09-08 19:24:33 +02:00
}
void cProtocol180 :: SendPluginMessage ( const AString & a_Channel , const AString & a_Message )
{
ASSERT ( m_State == 3 ); // In game mode?
cPacketizer Pkt ( * this , 0x3f );
Pkt . WriteString ( a_Channel );
Pkt . WriteBuf ( a_Message . data (), a_Message . size ());
}
void cProtocol180 :: SendRemoveEntityEffect ( const cEntity & a_Entity , int a_EffectID )
{
ASSERT ( m_State == 3 ); // In game mode?
cPacketizer Pkt ( * this , 0x1e );
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( a_Entity . GetUniqueID ());
2015-07-29 09:04:03 -06:00
Pkt . WriteBEUInt8 ( static_cast < UInt8 > ( a_EffectID ));
2014-09-08 19:24:33 +02:00
}
2015-04-08 00:41:19 +10:00
void cProtocol180 :: SendResetTitle ( void )
{
ASSERT ( m_State == 3 ); // In game mode?
cPacketizer Pkt ( * this , 0x45 ); // Title packet
Pkt . WriteVarInt32 ( 4 ); // Reset title
}
2014-09-08 19:24:33 +02:00
void cProtocol180 :: SendRespawn ( eDimension a_Dimension , bool a_ShouldIgnoreDimensionChecks )
{
if (( m_LastSentDimension == a_Dimension ) && ! a_ShouldIgnoreDimensionChecks )
{
// Must not send a respawn for the world with the same dimension, the client goes cuckoo if we do (unless we are respawning from death)
return ;
}
cPacketizer Pkt ( * this , 0x07 ); // Respawn packet
cPlayer * Player = m_Client -> GetPlayer ();
2015-07-29 09:04:03 -06:00
Pkt . WriteBEInt32 ( static_cast < Int32 > ( a_Dimension ));
2015-03-22 23:09:23 +01:00
Pkt . WriteBEUInt8 ( 2 ); // TODO: Difficulty (set to Normal)
2015-07-29 09:04:03 -06:00
Pkt . WriteBEUInt8 ( static_cast < Byte > ( Player -> GetEffectiveGameMode ()));
2014-09-08 19:24:33 +02:00
Pkt . WriteString ( "default" );
m_LastSentDimension = a_Dimension ;
}
void cProtocol180 :: SendExperience ( void )
{
ASSERT ( m_State == 3 ); // In game mode?
cPacketizer Pkt ( * this , 0x1f ); // Experience Packet
cPlayer * Player = m_Client -> GetPlayer ();
2015-03-22 23:09:23 +01:00
Pkt . WriteBEFloat ( Player -> GetXpPercentage ());
2015-07-29 09:04:03 -06:00
Pkt . WriteVarInt32 ( static_cast < UInt32 > ( Player -> GetXpLevel ()));
Pkt . WriteVarInt32 ( static_cast < UInt32 > ( Player -> GetCurrentXp ()));
2014-09-08 19:24:33 +02:00
}
void cProtocol180 :: SendExperienceOrb ( const cExpOrb & a_ExpOrb )
{
ASSERT ( m_State == 3 ); // In game mode?
cPacketizer Pkt ( * this , 0x11 );
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( a_ExpOrb . GetUniqueID ());
2014-09-08 19:24:33 +02:00
Pkt . WriteFPInt ( a_ExpOrb . GetPosX ());
Pkt . WriteFPInt ( a_ExpOrb . GetPosY ());
Pkt . WriteFPInt ( a_ExpOrb . GetPosZ ());
2015-07-29 09:04:03 -06:00
Pkt . WriteBEInt16 ( static_cast < Int16 > ( a_ExpOrb . GetReward ()));
2014-09-08 19:24:33 +02:00
}
void cProtocol180 :: SendScoreboardObjective ( const AString & a_Name , const AString & a_DisplayName , Byte a_Mode )
{
ASSERT ( m_State == 3 ); // In game mode?
cPacketizer Pkt ( * this , 0x3b );
Pkt . WriteString ( a_Name );
2015-03-22 23:09:23 +01:00
Pkt . WriteBEUInt8 ( a_Mode );
2014-09-13 21:48:16 +02:00
if (( a_Mode == 0 ) || ( a_Mode == 2 ))
{
Pkt . WriteString ( a_DisplayName );
Pkt . WriteString ( "integer" );
}
2014-09-08 19:24:33 +02:00
}
void cProtocol180 :: SendScoreUpdate ( const AString & a_Objective , const AString & a_Player , cObjective :: Score a_Score , Byte a_Mode )
{
ASSERT ( m_State == 3 ); // In game mode?
cPacketizer Pkt ( * this , 0x3c );
Pkt . WriteString ( a_Player );
2015-03-22 23:09:23 +01:00
Pkt . WriteBEUInt8 ( a_Mode );
2014-09-13 21:48:16 +02:00
Pkt . WriteString ( a_Objective );
2014-09-08 19:24:33 +02:00
if ( a_Mode != 1 )
{
2015-07-29 09:04:03 -06:00
Pkt . WriteVarInt32 ( static_cast < UInt32 > ( a_Score ));
2014-09-08 19:24:33 +02:00
}
}
void cProtocol180 :: SendDisplayObjective ( const AString & a_Objective , cScoreboard :: eDisplaySlot a_Display )
{
ASSERT ( m_State == 3 ); // In game mode?
cPacketizer Pkt ( * this , 0x3d );
2015-07-29 09:04:03 -06:00
Pkt . WriteBEUInt8 ( static_cast < UInt8 > ( a_Display ));
2014-09-08 19:24:33 +02:00
Pkt . WriteString ( a_Objective );
}
2015-04-08 00:41:19 +10:00
void cProtocol180 :: SendSetSubTitle ( const cCompositeChat & a_SubTitle )
{
SendSetRawSubTitle ( a_SubTitle . CreateJsonString ( false ));
}
void cProtocol180 :: SendSetRawSubTitle ( const AString & a_SubTitle )
{
ASSERT ( m_State == 3 ); // In game mode?
cPacketizer Pkt ( * this , 0x45 ); // Title packet
Pkt . WriteVarInt32 ( 1 ); // Set subtitle
Pkt . WriteString ( a_SubTitle );
}
void cProtocol180 :: SendSetTitle ( const cCompositeChat & a_Title )
{
SendSetRawTitle ( a_Title . CreateJsonString ( false ));
}
void cProtocol180 :: SendSetRawTitle ( const AString & a_Title )
{
ASSERT ( m_State == 3 ); // In game mode?
cPacketizer Pkt ( * this , 0x45 ); // Title packet
Pkt . WriteVarInt32 ( 0 ); // Set title
Pkt . WriteString ( a_Title );
}
2014-09-08 19:24:33 +02:00
void cProtocol180 :: SendSoundEffect ( const AString & a_SoundName , double a_X , double a_Y , double a_Z , float a_Volume , float a_Pitch )
{
ASSERT ( m_State == 3 ); // In game mode?
cPacketizer Pkt ( * this , 0x29 ); // Sound Effect packet
Pkt . WriteString ( a_SoundName );
2015-07-29 09:04:03 -06:00
Pkt . WriteBEInt32 ( static_cast < Int32 > ( a_X * 8.0 ));
Pkt . WriteBEInt32 ( static_cast < Int32 > ( a_Y * 8.0 ));
Pkt . WriteBEInt32 ( static_cast < Int32 > ( a_Z * 8.0 ));
2015-03-22 23:09:23 +01:00
Pkt . WriteBEFloat ( a_Volume );
2015-07-29 09:04:03 -06:00
Pkt . WriteBEUInt8 ( static_cast < Byte > ( a_Pitch * 63 ));
2014-09-08 19:24:33 +02:00
}
void cProtocol180 :: SendSoundParticleEffect ( int a_EffectID , int a_SrcX , int a_SrcY , int a_SrcZ , int a_Data )
{
ASSERT ( m_State == 3 ); // In game mode?
cPacketizer Pkt ( * this , 0x28 ); // Effect packet
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt32 ( a_EffectID );
Pkt . WritePosition64 ( a_SrcX , a_SrcY , a_SrcZ );
Pkt . WriteBEInt32 ( a_Data );
2014-09-08 19:24:33 +02:00
Pkt . WriteBool ( false );
}
void cProtocol180 :: SendSpawnFallingBlock ( const cFallingBlock & a_FallingBlock )
{
ASSERT ( m_State == 3 ); // In game mode?
cPacketizer Pkt ( * this , 0x0e ); // Spawn Object packet
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( a_FallingBlock . GetUniqueID ());
Pkt . WriteBEUInt8 ( 70 ); // Falling block
2014-09-08 19:24:33 +02:00
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 ());
2015-07-29 09:04:03 -06:00
Pkt . WriteBEInt32 ( static_cast < Int32 > ( a_FallingBlock . GetBlockType ()) | ( static_cast < Int32 > ( a_FallingBlock . GetBlockMeta ()) << 12 ));
Pkt . WriteBEInt16 ( static_cast < Int16 > ( a_FallingBlock . GetSpeedX () * 400 ));
Pkt . WriteBEInt16 ( static_cast < Int16 > ( a_FallingBlock . GetSpeedY () * 400 ));
Pkt . WriteBEInt16 ( static_cast < Int16 > ( a_FallingBlock . GetSpeedZ () * 400 ));
2014-09-08 19:24:33 +02:00
}
void cProtocol180 :: SendSpawnMob ( const cMonster & a_Mob )
{
ASSERT ( m_State == 3 ); // In game mode?
cPacketizer Pkt ( * this , 0x0f ); // Spawn Mob packet
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( a_Mob . GetUniqueID ());
2015-07-29 09:04:03 -06:00
Pkt . WriteBEUInt8 ( static_cast < Byte > ( a_Mob . GetMobType ()));
2014-09-08 19:24:33 +02:00
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 ());
2015-07-29 09:04:03 -06:00
Pkt . WriteBEInt16 ( static_cast < Int16 > ( a_Mob . GetSpeedX () * 400 ));
Pkt . WriteBEInt16 ( static_cast < Int16 > ( a_Mob . GetSpeedY () * 400 ));
Pkt . WriteBEInt16 ( static_cast < Int16 > ( a_Mob . GetSpeedZ () * 400 ));
2015-03-22 19:46:08 +01:00
WriteEntityMetadata ( Pkt , a_Mob );
2015-03-22 23:09:23 +01:00
Pkt . WriteBEUInt8 ( 0x7f ); // Metadata terminator
2014-09-08 19:24:33 +02:00
}
void cProtocol180 :: SendSpawnObject ( const cEntity & a_Entity , char a_ObjectType , int a_ObjectData , Byte a_Yaw , Byte a_Pitch )
{
ASSERT ( m_State == 3 ); // In game mode?
2014-09-11 20:06:28 +02:00
double PosX = a_Entity . GetPosX ();
double PosZ = a_Entity . GetPosZ ();
double Yaw = a_Entity . GetYaw ();
if ( a_ObjectType == 71 )
{
FixItemFramePositions ( a_ObjectData , PosX , PosZ , Yaw );
}
2014-09-11 17:03:09 +02:00
cPacketizer Pkt ( * this , 0xe ); // Spawn Object packet
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( a_Entity . GetUniqueID ());
2015-07-29 09:04:03 -06:00
Pkt . WriteBEUInt8 ( static_cast < UInt8 > ( a_ObjectType ));
2014-09-11 20:06:28 +02:00
Pkt . WriteFPInt ( PosX );
2014-09-08 19:24:33 +02:00
Pkt . WriteFPInt ( a_Entity . GetPosY ());
2014-09-11 20:06:28 +02:00
Pkt . WriteFPInt ( PosZ );
2014-09-08 19:24:33 +02:00
Pkt . WriteByteAngle ( a_Entity . GetPitch ());
2014-09-11 20:06:28 +02:00
Pkt . WriteByteAngle ( Yaw );
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt32 ( a_ObjectData );
2014-09-08 19:24:33 +02:00
if ( a_ObjectData != 0 )
{
2015-07-29 09:04:03 -06:00
Pkt . WriteBEInt16 ( static_cast < Int16 > ( a_Entity . GetSpeedX () * 400 ));
Pkt . WriteBEInt16 ( static_cast < Int16 > ( a_Entity . GetSpeedY () * 400 ));
Pkt . WriteBEInt16 ( static_cast < Int16 > ( a_Entity . GetSpeedZ () * 400 ));
2014-09-11 17:03:09 +02:00
}
2014-09-08 19:24:33 +02:00
}
void cProtocol180 :: SendSpawnVehicle ( const cEntity & a_Vehicle , char a_VehicleType , char a_VehicleSubType )
{
ASSERT ( m_State == 3 ); // In game mode?
cPacketizer Pkt ( * this , 0xe ); // Spawn Object packet
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( a_Vehicle . GetUniqueID ());
2015-07-29 09:04:03 -06:00
Pkt . WriteBEUInt8 ( static_cast < UInt8 > ( a_VehicleType ));
2014-09-08 19:24:33 +02:00
Pkt . WriteFPInt ( a_Vehicle . GetPosX ());
Pkt . WriteFPInt ( a_Vehicle . GetPosY ());
Pkt . WriteFPInt ( a_Vehicle . GetPosZ ());
Pkt . WriteByteAngle ( a_Vehicle . GetPitch ());
Pkt . WriteByteAngle ( a_Vehicle . GetYaw ());
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt32 ( a_VehicleSubType );
2014-09-08 19:24:33 +02:00
if ( a_VehicleSubType != 0 )
{
2015-07-29 09:04:03 -06:00
Pkt . WriteBEInt16 ( static_cast < Int16 > ( a_Vehicle . GetSpeedX () * 400 ));
Pkt . WriteBEInt16 ( static_cast < Int16 > ( a_Vehicle . GetSpeedY () * 400 ));
Pkt . WriteBEInt16 ( static_cast < Int16 > ( a_Vehicle . GetSpeedZ () * 400 ));
2014-09-08 19:24:33 +02:00
}
}
void cProtocol180 :: SendStatistics ( const cStatManager & a_Manager )
{
ASSERT ( m_State == 3 ); // In game mode?
cPacketizer Pkt ( * this , 0x37 );
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( statCount ); // TODO 2014-05-11 xdot: Optimization: Send "dirty" statistics only
2014-09-08 19:24:33 +02:00
2015-07-29 09:04:03 -06:00
size_t Count = static_cast < size_t > ( statCount );
for ( size_t i = 0 ; i < Count ; ++ i )
2014-09-08 19:24:33 +02:00
{
2015-07-29 09:04:03 -06:00
StatValue Value = a_Manager . GetValue ( static_cast < eStatistic > ( i ));
const AString & StatName = cStatInfo :: GetName ( static_cast < eStatistic > ( i ));
2014-09-08 19:24:33 +02:00
Pkt . WriteString ( StatName );
2015-07-29 09:04:03 -06:00
Pkt . WriteVarInt32 ( static_cast < UInt32 > ( Value ));
2014-09-08 19:24:33 +02:00
}
}
void cProtocol180 :: SendTabCompletionResults ( const AStringVector & a_Results )
{
ASSERT ( m_State == 3 ); // In game mode?
cPacketizer Pkt ( * this , 0x3a ); // Tab-Complete packet
2015-07-29 09:04:03 -06:00
Pkt . WriteVarInt32 ( static_cast < UInt32 > ( a_Results . size ()));
2014-09-08 19:24:33 +02:00
for ( AStringVector :: const_iterator itr = a_Results . begin (), end = a_Results . end (); itr != end ; ++ itr )
{
Pkt . WriteString ( * itr );
}
}
void cProtocol180 :: SendTeleportEntity ( const cEntity & a_Entity )
{
ASSERT ( m_State == 3 ); // In game mode?
2014-09-09 18:27:31 +02:00
cPacketizer Pkt ( * this , 0x18 );
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( a_Entity . GetUniqueID ());
2014-09-08 19:24:33 +02:00
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 ());
2015-02-06 21:40:20 +01:00
Pkt . WriteBool ( a_Entity . IsOnGround ());
2014-09-08 19:24:33 +02:00
}
void cProtocol180 :: SendThunderbolt ( int a_BlockX , int a_BlockY , int a_BlockZ )
{
ASSERT ( m_State == 3 ); // In game mode?
cPacketizer Pkt ( * this , 0x2c ); // Spawn Global Entity packet
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( 0 ); // EntityID = 0, always
Pkt . WriteBEUInt8 ( 1 ); // Type = Thunderbolt
2014-09-08 19:24:33 +02:00
Pkt . WriteFPInt ( a_BlockX );
Pkt . WriteFPInt ( a_BlockY );
Pkt . WriteFPInt ( a_BlockZ );
}
2015-04-08 00:41:19 +10:00
void cProtocol180 :: SendTitleTimes ( int a_FadeInTicks , int a_DisplayTicks , int a_FadeOutTicks )
{
ASSERT ( m_State == 3 ); // In game mode?
cPacketizer Pkt ( * this , 0x45 ); // Title packet
Pkt . WriteVarInt32 ( 2 ); // Set title display times
Pkt . WriteBEInt32 ( a_FadeInTicks );
Pkt . WriteBEInt32 ( a_DisplayTicks );
Pkt . WriteBEInt32 ( a_FadeOutTicks );
}
2014-09-08 19:24:33 +02:00
void cProtocol180 :: SendTimeUpdate ( Int64 a_WorldAge , Int64 a_TimeOfDay , bool a_DoDaylightCycle )
{
ASSERT ( m_State == 3 ); // In game mode?
if ( ! a_DoDaylightCycle )
{
// When writing a "-" before the number the client ignores it but it will stop the client-side time expiration.
a_TimeOfDay = std :: min ( - a_TimeOfDay , - 1LL );
}
cPacketizer Pkt ( * this , 0x03 );
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt64 ( a_WorldAge );
Pkt . WriteBEInt64 ( a_TimeOfDay );
2014-09-08 19:24:33 +02:00
}
void cProtocol180 :: SendUnloadChunk ( int a_ChunkX , int a_ChunkZ )
{
ASSERT ( m_State == 3 ); // In game mode?
cPacketizer Pkt ( * this , 0x21 ); // Chunk Data packet
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt32 ( a_ChunkX );
Pkt . WriteBEInt32 ( a_ChunkZ );
2014-09-08 19:24:33 +02:00
Pkt . WriteBool ( true );
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt16 ( 0 ); // Primary bitmap
Pkt . WriteVarInt32 ( 0 ); // Data size
2014-09-08 19:24:33 +02:00
}
void cProtocol180 :: SendUpdateBlockEntity ( cBlockEntity & a_BlockEntity )
{
ASSERT ( m_State == 3 ); // In game mode?
cPacketizer Pkt ( * this , 0x35 ); // Update tile entity packet
2015-03-22 23:09:23 +01:00
Pkt . WritePosition64 ( a_BlockEntity . GetPosX (), a_BlockEntity . GetPosY (), a_BlockEntity . GetPosZ ());
2014-09-08 19:24:33 +02:00
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
case E_BLOCK_BEACON : Action = 3 ; break ; // Update beacon entity
case E_BLOCK_HEAD : Action = 4 ; break ; // Update Mobhead entity
case E_BLOCK_FLOWER_POT : Action = 5 ; break ; // Update flower pot
default : ASSERT ( ! "Unhandled or unimplemented BlockEntity update request!" ); break ;
}
2015-03-22 23:09:23 +01:00
Pkt . WriteBEUInt8 ( Action );
2014-09-08 19:24:33 +02:00
2015-03-22 19:46:08 +01:00
WriteBlockEntity ( Pkt , a_BlockEntity );
2014-09-08 19:24:33 +02:00
}
void cProtocol180 :: SendUpdateSign ( int a_BlockX , int a_BlockY , int a_BlockZ , const AString & a_Line1 , const AString & a_Line2 , const AString & a_Line3 , const AString & a_Line4 )
{
ASSERT ( m_State == 3 ); // In game mode?
2014-09-25 18:49:55 +02:00
2014-09-08 19:24:33 +02:00
cPacketizer Pkt ( * this , 0x33 );
2015-03-22 23:09:23 +01:00
Pkt . WritePosition64 ( a_BlockX , a_BlockY , a_BlockZ );
2014-09-25 18:49:55 +02:00
Json :: StyledWriter JsonWriter ;
AString Lines [] = { a_Line1 , a_Line2 , a_Line3 , a_Line4 };
for ( size_t i = 0 ; i < ARRAYCOUNT ( Lines ); i ++ )
{
Json :: Value RootValue ;
RootValue [ "text" ] = Lines [ i ];
Pkt . WriteString ( JsonWriter . write ( RootValue ). c_str ());
}
2014-09-08 19:24:33 +02:00
}
void cProtocol180 :: SendUseBed ( const cEntity & a_Entity , int a_BlockX , int a_BlockY , int a_BlockZ )
{
ASSERT ( m_State == 3 ); // In game mode?
cPacketizer Pkt ( * this , 0x0a );
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( a_Entity . GetUniqueID ());
Pkt . WritePosition64 ( a_BlockX , a_BlockY , a_BlockZ );
2014-09-08 19:24:33 +02:00
}
void cProtocol180 :: SendWeather ( eWeather a_Weather )
{
ASSERT ( m_State == 3 ); // In game mode?
{
cPacketizer Pkt ( * this , 0x2b ); // Change Game State packet
2015-03-22 23:09:23 +01:00
Pkt . WriteBEUInt8 (( a_Weather == wSunny ) ? 1 : 2 ); // End rain / begin rain
Pkt . WriteBEFloat ( 0 ); // Unused for weather
2014-09-08 19:24:33 +02:00
}
// TODO: Fade effect, somehow
}
void cProtocol180 :: SendWholeInventory ( const cWindow & a_Window )
{
ASSERT ( m_State == 3 ); // In game mode?
cPacketizer Pkt ( * this , 0x30 ); // Window Items packet
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt8 ( a_Window . GetWindowID ());
2015-07-29 09:04:03 -06:00
Pkt . WriteBEInt16 ( static_cast < Int16 > ( a_Window . GetNumSlots ()));
2014-09-08 19:24:33 +02:00
cItems Slots ;
a_Window . GetSlots ( * ( m_Client -> GetPlayer ()), Slots );
for ( cItems :: const_iterator itr = Slots . begin (), end = Slots . end (); itr != end ; ++ itr )
{
2015-03-22 19:46:08 +01:00
WriteItem ( Pkt , * itr );
2014-09-08 19:24:33 +02:00
} // for itr - Slots[]
}
void cProtocol180 :: SendWindowClose ( const cWindow & a_Window )
{
ASSERT ( m_State == 3 ); // In game mode?
cPacketizer Pkt ( * this , 0x2e );
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt8 ( a_Window . GetWindowID ());
2014-09-08 19:24:33 +02:00
}
void cProtocol180 :: SendWindowOpen ( const cWindow & a_Window )
{
ASSERT ( m_State == 3 ); // In game mode?
2014-09-12 01:15:21 +02:00
2014-09-08 19:24:33 +02:00
if ( a_Window . GetWindowType () < 0 )
{
// Do not send this packet for player inventory windows
return ;
}
2014-09-12 01:15:21 +02:00
2014-09-08 19:24:33 +02:00
cPacketizer Pkt ( * this , 0x2d );
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt8 ( a_Window . GetWindowID ());
2014-09-11 23:17:27 +02:00
Pkt . WriteString ( a_Window . GetWindowTypeName ());
Pkt . WriteString ( Printf ( "{ \" text \" : \" %s \" }" , a_Window . GetWindowTitle (). c_str ()));
2014-09-12 01:15:21 +02:00
switch ( a_Window . GetWindowType ())
{
case cWindow :: wtWorkbench :
case cWindow :: wtEnchantment :
case cWindow :: wtAnvil :
{
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt8 ( 0 );
2014-09-12 01:15:21 +02:00
break ;
}
default :
{
2015-07-29 09:04:03 -06:00
Pkt . WriteBEInt8 ( static_cast < Int8 > ( a_Window . GetNumNonInventorySlots ()));
2014-09-12 01:15:21 +02:00
break ;
}
}
2014-09-08 19:24:33 +02:00
if ( a_Window . GetWindowType () == cWindow :: wtAnimalChest )
{
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt32 ( 0 ); // TODO: The animal's EntityID
2014-09-08 19:24:33 +02:00
}
}
2014-10-03 21:32:41 +01:00
void cProtocol180 :: SendWindowProperty ( const cWindow & a_Window , short a_Property , short a_Value )
2014-09-08 19:24:33 +02:00
{
ASSERT ( m_State == 3 ); // In game mode?
cPacketizer Pkt ( * this , 0x31 ); // Window Property packet
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt8 ( a_Window . GetWindowID ());
Pkt . WriteBEInt16 ( a_Property );
Pkt . WriteBEInt16 ( a_Value );
2014-09-08 19:24:33 +02:00
}
2014-09-09 18:27:31 +02:00
bool cProtocol180 :: CompressPacket ( const AString & a_Packet , AString & a_CompressedData )
{
// Compress the data:
2014-09-19 15:07:01 +02:00
char CompressedData [ MAX_COMPRESSED_PACKET_LEN ];
2014-09-09 18:27:31 +02:00
2015-07-07 11:50:06 +02:00
uLongf CompressedSize = compressBound ( static_cast < uLongf > ( a_Packet . size ()));
2014-09-19 15:07:01 +02:00
if ( CompressedSize >= MAX_COMPRESSED_PACKET_LEN )
2014-09-09 18:27:31 +02:00
{
ASSERT ( ! "Too high packet size." );
return false ;
}
2015-03-22 23:09:23 +01:00
int Status = compress2 (
reinterpret_cast < Bytef *> ( CompressedData ), & CompressedSize ,
2015-07-07 11:50:06 +02:00
reinterpret_cast < const Bytef *> ( a_Packet . data ()), static_cast < uLongf > ( a_Packet . size ()), Z_DEFAULT_COMPRESSION
2015-03-22 23:09:23 +01:00
);
2014-09-09 18:27:31 +02:00
if ( Status != Z_OK )
{
return false ;
}
AString LengthData ;
cByteBuffer Buffer ( 20 );
2015-03-22 23:09:23 +01:00
Buffer . WriteVarInt32 ( static_cast < UInt32 > ( a_Packet . size ()));
2014-09-09 18:27:31 +02:00
Buffer . ReadAll ( LengthData );
Buffer . CommitRead ();
2015-07-07 11:50:06 +02:00
Buffer . WriteVarInt32 ( static_cast < UInt32 > ( CompressedSize + LengthData . size ()));
2015-03-22 23:09:23 +01:00
Buffer . WriteVarInt32 ( static_cast < UInt32 > ( a_Packet . size ()));
2014-09-09 18:27:31 +02:00
Buffer . ReadAll ( LengthData );
Buffer . CommitRead ();
a_CompressedData . clear ();
2014-09-19 15:07:01 +02:00
a_CompressedData . reserve ( LengthData . size () + CompressedSize );
2014-09-09 18:27:31 +02:00
a_CompressedData . append ( LengthData . data (), LengthData . size ());
a_CompressedData . append ( CompressedData , CompressedSize );
return true ;
}
2014-09-11 17:03:09 +02:00
int cProtocol180 :: GetParticleID ( const AString & a_ParticleName )
{
static bool IsInitialized = false ;
static std :: map < AString , int > ParticleMap ;
if ( ! IsInitialized )
{
// Initialize the ParticleMap:
ParticleMap [ "explode" ] = 0 ;
ParticleMap [ "largeexplode" ] = 1 ;
ParticleMap [ "hugeexplosion" ] = 2 ;
ParticleMap [ "fireworksspark" ] = 3 ;
ParticleMap [ "bubble" ] = 4 ;
ParticleMap [ "splash" ] = 5 ;
ParticleMap [ "wake" ] = 6 ;
ParticleMap [ "suspended" ] = 7 ;
ParticleMap [ "depthsuspend" ] = 8 ;
ParticleMap [ "crit" ] = 9 ;
ParticleMap [ "magiccrit" ] = 10 ;
ParticleMap [ "smoke" ] = 11 ;
ParticleMap [ "largesmoke" ] = 12 ;
ParticleMap [ "spell" ] = 13 ;
ParticleMap [ "instantspell" ] = 14 ;
ParticleMap [ "mobspell" ] = 15 ;
ParticleMap [ "mobspellambient" ] = 16 ;
ParticleMap [ "witchmagic" ] = 17 ;
ParticleMap [ "dripwater" ] = 18 ;
ParticleMap [ "driplava" ] = 19 ;
ParticleMap [ "angryvillager" ] = 20 ;
ParticleMap [ "happyVillager" ] = 21 ;
ParticleMap [ "townaura" ] = 22 ;
ParticleMap [ "note" ] = 23 ;
ParticleMap [ "portal" ] = 24 ;
ParticleMap [ "enchantmenttable" ] = 25 ;
ParticleMap [ "flame" ] = 26 ;
ParticleMap [ "lava" ] = 27 ;
ParticleMap [ "footstep" ] = 28 ;
ParticleMap [ "cloud" ] = 29 ;
ParticleMap [ "reddust" ] = 30 ;
ParticleMap [ "snowballpoof" ] = 31 ;
ParticleMap [ "snowshovel" ] = 32 ;
ParticleMap [ "slime" ] = 33 ;
ParticleMap [ "heart" ] = 34 ;
ParticleMap [ "barrier" ] = 35 ;
ParticleMap [ "iconcrack" ] = 36 ;
ParticleMap [ "blockcrack" ] = 37 ;
ParticleMap [ "blockdust" ] = 38 ;
ParticleMap [ "droplet" ] = 39 ;
ParticleMap [ "take" ] = 40 ;
ParticleMap [ "mobappearance" ] = 41 ;
}
AString ParticleName = StrToLower ( a_ParticleName );
if ( ParticleMap . find ( ParticleName ) == ParticleMap . end ())
{
LOGWARNING ( "Unknown particle: %s" , a_ParticleName . c_str ());
2014-09-14 20:08:18 +02:00
ASSERT ( ! "Unknown particle" );
2014-09-11 17:03:09 +02:00
return 0 ;
}
return ParticleMap [ ParticleName ];
}
2014-09-11 20:06:28 +02:00
void cProtocol180 :: FixItemFramePositions ( int a_ObjectData , double & a_PosX , double & a_PosZ , double & a_Yaw )
{
switch ( a_ObjectData )
{
case 0 :
{
a_PosZ += 1 ;
a_Yaw = 0 ;
break ;
}
case 1 :
{
a_PosX -= 1 ;
a_Yaw = 90 ;
break ;
}
case 2 :
{
a_PosZ -= 1 ;
a_Yaw = 180 ;
break ;
}
case 3 :
{
a_PosX += 1 ;
a_Yaw = 270 ;
break ;
}
}
}
2014-09-08 19:24:33 +02:00
void cProtocol180 :: AddReceivedData ( const char * a_Data , size_t a_Size )
{
// Write the incoming data into the comm log file:
2015-01-24 15:59:41 +01:00
if ( g_ShouldLogCommIn && m_CommLogFile . IsOpen ())
2014-09-08 19:24:33 +02:00
{
if ( m_ReceivedData . GetReadableSpace () > 0 )
{
AString AllData ;
size_t 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 ( "Incoming data, " SIZE_T_FMT " (0x" SIZE_T_FMT_HEX ") unparsed bytes already present in buffer: \n %s \n " ,
AllData . size (), AllData . size (), Hex . c_str ()
);
}
AString Hex ;
CreateHexDump ( Hex , a_Data , a_Size , 16 );
2015-07-29 09:04:03 -06:00
m_CommLogFile . Printf ( "Incoming data: %u (0x%x) bytes: \n %s \n " ,
static_cast < unsigned > ( a_Size ), static_cast < unsigned > ( a_Size ), Hex . c_str ()
2014-09-08 19:24:33 +02:00
);
m_CommLogFile . Flush ();
}
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:
for (;;)
{
UInt32 PacketLen ;
if ( ! m_ReceivedData . ReadVarInt ( PacketLen ))
{
// Not enough data
m_ReceivedData . ResetRead ();
break ;
}
if ( ! m_ReceivedData . CanReadBytes ( PacketLen ))
{
// The full packet hasn't been received yet
m_ReceivedData . ResetRead ();
break ;
}
2014-09-25 20:34:49 +02:00
// Check packet for compression:
UInt32 CompressedSize = 0 ;
AString UncompressedData ;
2014-09-08 19:24:33 +02:00
if ( m_State == 3 )
{
2015-07-07 11:50:06 +02:00
UInt32 NumBytesRead = static_cast < UInt32 > ( m_ReceivedData . GetReadableSpace ());
2014-09-25 20:34:49 +02:00
m_ReceivedData . ReadVarInt ( CompressedSize );
2014-09-28 22:25:48 +02:00
if ( CompressedSize > PacketLen )
{
m_Client -> Kick ( "Bad compression" );
return ;
}
2014-09-25 20:34:49 +02:00
if ( CompressedSize > 0 )
2014-09-04 00:29:36 +02:00
{
2014-09-25 20:34:49 +02:00
// Decompress the data:
AString CompressedData ;
2015-06-18 11:19:41 +01:00
if ( ! m_ReceivedData . ReadString ( CompressedData , CompressedSize ) || ( InflateString ( CompressedData . data (), CompressedSize , UncompressedData ) != Z_OK ))
2014-12-21 20:01:42 +01:00
{
m_Client -> Kick ( "Compression failure" );
return ;
}
2015-07-07 11:50:06 +02:00
PacketLen = static_cast < UInt32 > ( UncompressedData . size ());
2014-09-04 00:29:36 +02:00
}
2014-09-25 20:34:49 +02:00
else
{
2015-07-07 11:50:06 +02:00
NumBytesRead -= static_cast < UInt32 > ( m_ReceivedData . GetReadableSpace ()); // How many bytes has the CompressedSize taken up?
2014-09-25 20:34:49 +02:00
ASSERT ( PacketLen > NumBytesRead );
PacketLen -= NumBytesRead ;
}
}
// Move the packet payload to a separate cByteBuffer, bb:
cByteBuffer bb ( PacketLen + 1 );
if ( CompressedSize == 0 )
{
// No compression was used, move directly
2015-07-29 09:04:03 -06:00
VERIFY ( m_ReceivedData . ReadToByteBuffer ( bb , static_cast < size_t > ( PacketLen )));
2014-09-08 19:24:33 +02:00
}
2014-09-25 20:34:49 +02:00
else
{
// Compression was used, move the uncompressed data:
VERIFY ( bb . Write ( UncompressedData . data (), UncompressedData . size ()));
}
m_ReceivedData . CommitRead ();
2014-09-08 19:24:33 +02:00
UInt32 PacketType ;
if ( ! bb . ReadVarInt ( PacketType ))
{
// Not enough data
break ;
}
2014-09-25 18:34:40 +02:00
// Write one NUL extra, so that we can detect over-reads
bb . Write ( " \0 " , 1 );
2014-09-08 19:24:33 +02:00
// Log the packet info into the comm log file:
2015-01-24 15:59:41 +01:00
if ( g_ShouldLogCommIn && m_CommLogFile . IsOpen ())
2014-09-08 19:24:33 +02:00
{
AString PacketData ;
bb . ReadAll ( PacketData );
bb . ResetRead ();
2014-12-21 20:01:42 +01:00
bb . ReadVarInt ( PacketType ); // We have already read the packet type once, it will be there again
2014-09-25 18:34:40 +02:00
ASSERT ( PacketData . size () > 0 ); // We have written an extra NUL, so there had to be at least one byte read
2014-09-08 19:24:33 +02:00
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 ()
);
}
if ( ! HandlePacket ( bb , PacketType ))
{
// Unknown packet, already been reported, but without the length. Log the length here:
LOGWARNING ( "Unhandled packet: type 0x%x, state %d, length %u" , PacketType , m_State , PacketLen );
2014-09-04 00:29:36 +02:00
2014-09-08 19:24:33 +02: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
// Put a message in the comm log:
2015-01-24 15:59:41 +01:00
if ( g_ShouldLogCommIn && m_CommLogFile . IsOpen ())
2014-09-04 00:29:36 +02:00
{
2014-09-08 19:24:33 +02:00
m_CommLogFile . Printf ( "^^^^^^ Unhandled packet ^^^^^^ \n\n\n " );
2014-09-04 00:29:36 +02:00
}
2014-09-08 19:24:33 +02:00
return ;
}
2014-09-25 18:34:40 +02:00
// The packet should have 1 byte left in the buffer - the NUL we had added
if ( bb . GetReadableSpace () != 1 )
2014-09-08 19:24:33 +02:00
{
// Read more or less than packet length, report as error
2014-09-12 02:42:04 +02:00
LOGWARNING ( "Protocol 1.8: Wrong number of bytes read for packet 0x%x, state %d. Read " SIZE_T_FMT " bytes, packet contained %u bytes" ,
2014-09-08 19:24:33 +02:00
PacketType , m_State , bb . GetUsedSpace () - bb . GetReadableSpace (), PacketLen
);
// Put a message in the comm log:
2015-01-24 15:59:41 +01:00
if ( g_ShouldLogCommIn && m_CommLogFile . IsOpen ())
2014-09-04 00:29:36 +02:00
{
2014-09-08 19:24:33 +02:00
m_CommLogFile . Printf ( "^^^^^^ Wrong number of bytes read for this packet (exp %d left, got " SIZE_T_FMT " left) ^^^^^^ \n\n\n " ,
1 , bb . GetReadableSpace ()
);
m_CommLogFile . Flush ();
}
ASSERT ( ! "Read wrong number of bytes!" );
m_Client -> PacketError ( PacketType );
}
} // for (ever)
// Log any leftover bytes into the logfile:
2015-01-24 15:59:41 +01:00
if ( g_ShouldLogCommIn && ( m_ReceivedData . GetReadableSpace () > 0 ) && m_CommLogFile . IsOpen ())
2014-09-08 19:24:33 +02:00
{
AString AllData ;
size_t 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 " SIZE_T_FMT " (0x" SIZE_T_FMT_HEX ") bytes of non-parse-able data left in the buffer: \n %s" ,
m_ReceivedData . GetReadableSpace (), m_ReceivedData . GetReadableSpace (), Hex . c_str ()
);
m_CommLogFile . Flush ();
}
}
bool cProtocol180 :: HandlePacket ( cByteBuffer & a_ByteBuffer , UInt32 a_PacketType )
{
switch ( m_State )
{
case 1 :
{
// Status
switch ( a_PacketType )
{
case 0x00 : HandlePacketStatusRequest ( a_ByteBuffer ); return true ;
case 0x01 : HandlePacketStatusPing ( a_ByteBuffer ); return true ;
}
break ;
}
case 2 :
{
// Login
switch ( a_PacketType )
{
case 0x00 : HandlePacketLoginStart ( a_ByteBuffer ); return true ;
case 0x01 : HandlePacketLoginEncryptionResponse ( a_ByteBuffer ); return true ;
}
break ;
}
case 3 :
{
// Game
switch ( a_PacketType )
{
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 ;
case 0x0f : // Confirm transaction - not used in MCS
case 0x10 : HandlePacketCreativeInventoryAction ( a_ByteBuffer ); return true ;
case 0x11 : HandlePacketEnchantItem ( 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 ;
2014-09-04 00:29:36 +02:00
}
2014-09-08 19:24:33 +02:00
break ;
}
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 ;
}
} // switch (m_State)
// Unknown packet type, report to the ClientHandle:
m_Client -> PacketUnknown ( a_PacketType );
return false ;
}
void cProtocol180 :: HandlePacketStatusPing ( cByteBuffer & a_ByteBuffer )
{
HANDLE_READ ( a_ByteBuffer , ReadBEInt64 , Int64 , Timestamp );
cPacketizer Pkt ( * this , 0x01 ); // Ping packet
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt64 ( Timestamp );
2014-09-08 19:24:33 +02:00
}
void cProtocol180 :: HandlePacketStatusRequest ( cByteBuffer & a_ByteBuffer )
{
cServer * Server = cRoot :: Get () -> GetServer ();
AString ServerDescription = Server -> GetDescription ();
int NumPlayers = Server -> GetNumPlayers ();
int MaxPlayers = Server -> GetMaxPlayers ();
AString Favicon = Server -> GetFaviconData ();
cRoot :: Get () -> GetPluginManager () -> CallHookServerPing ( * m_Client , ServerDescription , NumPlayers , MaxPlayers , Favicon );
// Version:
Json :: Value Version ;
2015-09-25 10:14:17 +02:00
Version [ "name" ] = "Cuberite 1.8" ;
2014-09-08 19:24:33 +02:00
Version [ "protocol" ] = 47 ;
// Players:
Json :: Value Players ;
Players [ "online" ] = NumPlayers ;
Players [ "max" ] = MaxPlayers ;
// TODO: Add "sample"
// Description:
Json :: Value Description ;
Description [ "text" ] = ServerDescription . c_str ();
// Create the response:
Json :: Value ResponseValue ;
ResponseValue [ "version" ] = Version ;
ResponseValue [ "players" ] = Players ;
ResponseValue [ "description" ] = Description ;
if ( ! Favicon . empty ())
{
ResponseValue [ "favicon" ] = Printf ( "data:image/png;base64,%s" , Favicon . c_str ());
}
Json :: StyledWriter Writer ;
AString Response = Writer . write ( ResponseValue );
cPacketizer Pkt ( * this , 0x00 ); // Response packet
Pkt . WriteString ( Response );
}
void cProtocol180 :: HandlePacketLoginEncryptionResponse ( cByteBuffer & a_ByteBuffer )
{
UInt32 EncKeyLength , EncNonceLength ;
2014-09-25 19:42:35 +02:00
if ( ! a_ByteBuffer . ReadVarInt ( EncKeyLength ))
{
return ;
}
2014-09-08 19:24:33 +02:00
AString EncKey ;
if ( ! a_ByteBuffer . ReadString ( EncKey , EncKeyLength ))
{
return ;
}
2014-09-25 19:42:35 +02:00
if ( ! a_ByteBuffer . ReadVarInt ( EncNonceLength ))
{
return ;
}
2014-09-08 19:24:33 +02:00
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 )];
2015-07-29 09:04:03 -06:00
int res = rsaDecryptor . Decrypt ( reinterpret_cast < const Byte *> ( EncNonce . data ()), EncNonce . size (), reinterpret_cast < Byte *> ( DecryptedNonce ), sizeof ( DecryptedNonce ));
2014-09-08 19:24:33 +02:00
if ( res != 4 )
{
LOGD ( "Bad nonce length: got %d, exp %d" , res , 4 );
m_Client -> Kick ( "Hacked client" );
return ;
}
2015-08-05 01:24:59 +03:00
if ( ntohl ( DecryptedNonce [ 0 ]) != static_cast < unsigned > ( reinterpret_cast < uintptr_t > ( this )))
2014-09-08 19:24:33 +02:00
{
LOGD ( "Bad nonce value" );
m_Client -> Kick ( "Hacked client" );
return ;
}
// Decrypt the symmetric encryption key using privkey:
Byte DecryptedKey [ MAX_ENC_LEN ];
2015-07-29 09:04:03 -06:00
res = rsaDecryptor . Decrypt ( reinterpret_cast < const Byte *> ( EncKey . data ()), EncKey . size (), DecryptedKey , sizeof ( DecryptedKey ));
2014-09-08 19:24:33 +02:00
if ( res != 16 )
{
LOGD ( "Bad key length" );
m_Client -> Kick ( "Hacked client" );
return ;
}
StartEncryption ( DecryptedKey );
m_Client -> HandleLogin ( 4 , m_Client -> GetUsername ());
}
void cProtocol180 :: HandlePacketLoginStart ( cByteBuffer & a_ByteBuffer )
{
AString Username ;
if ( ! a_ByteBuffer . ReadVarUTF8String ( Username ))
{
m_Client -> Kick ( "Bad username" );
return ;
}
if ( ! m_Client -> HandleHandshake ( Username ))
{
// The client is not welcome here, they have been sent a Kick packet already
return ;
}
cServer * Server = cRoot :: Get () -> GetServer ();
// If auth is required, then send the encryption request:
if ( Server -> ShouldAuthenticate ())
{
cPacketizer Pkt ( * this , 0x01 );
Pkt . WriteString ( Server -> GetServerID ());
const AString & PubKeyDer = Server -> GetPublicKeyDER ();
2015-07-29 09:04:03 -06:00
Pkt . WriteVarInt32 ( static_cast < UInt32 > ( PubKeyDer . size ()));
2014-09-08 19:24:33 +02:00
Pkt . WriteBuf ( PubKeyDer . data (), PubKeyDer . size ());
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( 4 );
2015-08-05 01:24:59 +03:00
Pkt . WriteBEInt32 ( static_cast < int > ( reinterpret_cast < intptr_t > ( this ))); // Using 'this' as the cryptographic nonce, so that we don't have to generate one each time :)
2014-09-08 19:24:33 +02:00
m_Client -> SetUsername ( Username );
return ;
}
m_Client -> HandleLogin ( 4 , Username );
}
void cProtocol180 :: HandlePacketAnimation ( cByteBuffer & a_ByteBuffer )
{
2015-04-17 15:33:40 +01:00
m_Client -> HandleAnimation ( 0 ); // Packet exists solely for arm-swing notification
2014-09-08 19:24:33 +02:00
}
void cProtocol180 :: HandlePacketBlockDig ( cByteBuffer & a_ByteBuffer )
{
2015-03-21 13:00:20 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEUInt8 , UInt8 , Status );
2014-09-08 19:24:33 +02:00
int BlockX , BlockY , BlockZ ;
2015-03-22 23:09:23 +01:00
if ( ! a_ByteBuffer . ReadPosition64 ( BlockX , BlockY , BlockZ ))
2014-09-08 19:24:33 +02:00
{
return ;
}
2015-03-21 13:00:20 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEInt8 , Int8 , Face );
m_Client -> HandleLeftClick ( BlockX , BlockY , BlockZ , FaceIntToBlockFace ( Face ), Status );
2014-09-08 19:24:33 +02:00
}
void cProtocol180 :: HandlePacketBlockPlace ( cByteBuffer & a_ByteBuffer )
{
int BlockX , BlockY , BlockZ ;
2015-03-22 23:09:23 +01:00
if ( ! a_ByteBuffer . ReadPosition64 ( BlockX , BlockY , BlockZ ))
2014-09-08 19:24:33 +02:00
{
return ;
}
2015-03-21 13:00:20 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEInt8 , Int8 , Face );
2014-09-08 20:12:43 +02:00
2014-09-08 19:24:33 +02:00
cItem Item ;
2014-09-22 22:06:08 +02:00
ReadItem ( a_ByteBuffer , Item , 3 );
2014-09-08 19:24:33 +02:00
2015-03-21 13:00:20 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEUInt8 , UInt8 , CursorX );
HANDLE_READ ( a_ByteBuffer , ReadBEUInt8 , UInt8 , CursorY );
HANDLE_READ ( a_ByteBuffer , ReadBEUInt8 , UInt8 , CursorZ );
m_Client -> HandleRightClick ( BlockX , BlockY , BlockZ , FaceIntToBlockFace ( Face ), CursorX , CursorY , CursorZ , m_Client -> GetPlayer () -> GetEquippedItem ());
2014-09-08 19:24:33 +02:00
}
void cProtocol180 :: HandlePacketChatMessage ( cByteBuffer & a_ByteBuffer )
{
HANDLE_READ ( a_ByteBuffer , ReadVarUTF8String , AString , Message );
m_Client -> HandleChat ( Message );
}
void cProtocol180 :: HandlePacketClientSettings ( cByteBuffer & a_ByteBuffer )
{
HANDLE_READ ( a_ByteBuffer , ReadVarUTF8String , AString , Locale );
2015-03-21 13:00:20 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEUInt8 , UInt8 , ViewDistance );
HANDLE_READ ( a_ByteBuffer , ReadBEUInt8 , UInt8 , ChatFlags );
2014-09-08 19:24:33 +02:00
HANDLE_READ ( a_ByteBuffer , ReadBool , bool , ChatColors );
2015-03-21 13:00:20 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEUInt8 , UInt8 , SkinFlags );
2014-09-08 19:24:33 +02:00
m_Client -> SetLocale ( Locale );
2014-10-02 23:50:41 +02:00
m_Client -> SetViewDistance ( ViewDistance );
2014-09-08 19:24:33 +02:00
// TODO: Handle other values
}
void cProtocol180 :: HandlePacketClientStatus ( cByteBuffer & a_ByteBuffer )
{
2015-03-21 13:00:20 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEUInt8 , UInt8 , ActionID );
2014-09-08 19:24:33 +02:00
switch ( ActionID )
{
case 0 :
{
// Respawn
m_Client -> HandleRespawn ();
break ;
}
case 1 :
{
// Request stats
const cStatManager & Manager = m_Client -> GetPlayer () -> GetStatManager ();
SendStatistics ( Manager );
2014-09-04 00:29:36 +02:00
2014-09-08 19:24:33 +02:00
break ;
}
case 2 :
{
// Open Inventory achievement
m_Client -> GetPlayer () -> AwardAchievement ( achOpenInv );
break ;
}
}
}
2014-09-04 00:29:36 +02:00
2014-09-08 19:24:33 +02:00
void cProtocol180 :: HandlePacketCreativeInventoryAction ( cByteBuffer & a_ByteBuffer )
{
2015-03-21 13:00:20 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEInt16 , Int16 , SlotNum );
2014-09-08 19:24:33 +02:00
cItem Item ;
2014-09-22 22:06:08 +02:00
if ( ! ReadItem ( a_ByteBuffer , Item ))
2014-09-08 19:24:33 +02:00
{
return ;
}
2015-05-16 23:22:50 +02:00
m_Client -> HandleCreativeInventory ( SlotNum , Item , ( SlotNum == - 1 ) ? caLeftClickOutside : caLeftClick );
2014-09-08 19:24:33 +02:00
}
void cProtocol180 :: HandlePacketEntityAction ( cByteBuffer & a_ByteBuffer )
{
2015-03-21 13:00:20 +01:00
HANDLE_READ ( a_ByteBuffer , ReadVarInt , UInt32 , PlayerID );
HANDLE_READ ( a_ByteBuffer , ReadBEUInt8 , UInt8 , Action );
HANDLE_READ ( a_ByteBuffer , ReadVarInt , UInt32 , JumpBoost );
2014-09-08 19:24:33 +02:00
switch ( Action )
{
case 0 : m_Client -> HandleEntityCrouch ( PlayerID , true ); break ; // Crouch
case 1 : m_Client -> HandleEntityCrouch ( PlayerID , false ); break ; // Uncrouch
case 2 : m_Client -> HandleEntityLeaveBed ( PlayerID ); break ; // Leave Bed
case 3 : m_Client -> HandleEntitySprinting ( PlayerID , true ); break ; // Start sprinting
case 4 : m_Client -> HandleEntitySprinting ( PlayerID , false ); break ; // Stop sprinting
}
}
void cProtocol180 :: HandlePacketKeepAlive ( cByteBuffer & a_ByteBuffer )
{
HANDLE_READ ( a_ByteBuffer , ReadVarInt , UInt32 , KeepAliveID );
2015-08-05 01:24:59 +03:00
m_Client -> HandleKeepAlive ( KeepAliveID );
2014-09-08 19:24:33 +02:00
}
void cProtocol180 :: HandlePacketPlayer ( cByteBuffer & a_ByteBuffer )
{
HANDLE_READ ( a_ByteBuffer , ReadBool , bool , IsOnGround );
// TODO: m_Client->HandlePlayerOnGround(IsOnGround);
}
void cProtocol180 :: HandlePacketPlayerAbilities ( cByteBuffer & a_ByteBuffer )
{
2015-03-21 13:00:20 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEUInt8 , UInt8 , Flags );
2014-09-08 19:24:33 +02:00
HANDLE_READ ( a_ByteBuffer , ReadBEFloat , float , FlyingSpeed );
HANDLE_READ ( a_ByteBuffer , ReadBEFloat , float , WalkingSpeed );
2015-03-21 13:00:20 +01:00
// COnvert the bitfield into individual boolean flags:
2014-09-08 19:24:33 +02:00
bool IsFlying = false , CanFly = false ;
if (( Flags & 2 ) != 0 )
{
IsFlying = true ;
}
if (( Flags & 4 ) != 0 )
{
CanFly = true ;
}
m_Client -> HandlePlayerAbilities ( CanFly , IsFlying , FlyingSpeed , WalkingSpeed );
}
void cProtocol180 :: HandlePacketPlayerLook ( cByteBuffer & a_ByteBuffer )
{
HANDLE_READ ( a_ByteBuffer , ReadBEFloat , float , Yaw );
HANDLE_READ ( a_ByteBuffer , ReadBEFloat , float , Pitch );
HANDLE_READ ( a_ByteBuffer , ReadBool , bool , IsOnGround );
m_Client -> HandlePlayerLook ( Yaw , Pitch , IsOnGround );
}
void cProtocol180 :: HandlePacketPlayerPos ( cByteBuffer & a_ByteBuffer )
{
HANDLE_READ ( a_ByteBuffer , ReadBEDouble , double , PosX );
HANDLE_READ ( a_ByteBuffer , ReadBEDouble , double , PosY );
HANDLE_READ ( a_ByteBuffer , ReadBEDouble , double , PosZ );
HANDLE_READ ( a_ByteBuffer , ReadBool , bool , IsOnGround );
2015-05-03 18:56:37 +01:00
m_Client -> HandlePlayerPos ( PosX , PosY , PosZ , PosY + ( m_Client -> GetPlayer () -> IsCrouched () ? 1.54 : 1.62 ), IsOnGround );
2014-09-08 19:24:33 +02:00
}
void cProtocol180 :: HandlePacketPlayerPosLook ( cByteBuffer & a_ByteBuffer )
{
HANDLE_READ ( a_ByteBuffer , ReadBEDouble , double , PosX );
HANDLE_READ ( a_ByteBuffer , ReadBEDouble , double , PosY );
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 );
m_Client -> HandlePlayerMoveLook ( PosX , PosY , PosZ , PosY + 1.62 , Yaw , Pitch , IsOnGround );
}
void cProtocol180 :: HandlePacketPluginMessage ( cByteBuffer & a_ByteBuffer )
{
HANDLE_READ ( a_ByteBuffer , ReadVarUTF8String , AString , Channel );
2014-09-30 13:33:57 +02:00
// If the plugin channel is recognized vanilla, handle it directly:
2014-09-28 23:03:44 +02:00
if ( Channel . substr ( 0 , 3 ) == "MC|" )
2014-09-27 23:22:26 +02:00
{
2014-09-30 13:33:57 +02:00
HandleVanillaPluginMessage ( a_ByteBuffer , Channel );
2015-01-03 22:23:49 +01:00
// Skip any unread data (vanilla sometimes sends garbage at the end of a packet; #1692):
if ( a_ByteBuffer . GetReadableSpace () > 1 )
{
LOGD ( "Protocol 1.8: Skipping garbage data at the end of a vanilla PluginMessage packet, %u bytes" ,
2015-01-03 22:39:55 +01:00
static_cast < unsigned > ( a_ByteBuffer . GetReadableSpace () - 1 )
2015-01-03 22:23:49 +01:00
);
a_ByteBuffer . SkipRead ( a_ByteBuffer . GetReadableSpace () - 1 );
}
2014-09-30 13:33:57 +02:00
return ;
2014-09-27 23:22:26 +02:00
}
2014-09-30 13:33:57 +02:00
// Read the plugin message and relay to clienthandle:
AString Data ;
VERIFY ( a_ByteBuffer . ReadString ( Data , a_ByteBuffer . GetReadableSpace () - 1 )); // Always succeeds
2014-09-08 19:24:33 +02:00
m_Client -> HandlePluginMessage ( Channel , Data );
}
void cProtocol180 :: HandlePacketSlotSelect ( cByteBuffer & a_ByteBuffer )
{
2015-03-21 13:00:20 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEInt16 , Int16 , SlotNum );
2014-09-08 19:24:33 +02:00
m_Client -> HandleSlotSelected ( SlotNum );
}
void cProtocol180 :: HandlePacketSteerVehicle ( cByteBuffer & a_ByteBuffer )
{
HANDLE_READ ( a_ByteBuffer , ReadBEFloat , float , Forward );
HANDLE_READ ( a_ByteBuffer , ReadBEFloat , float , Sideways );
2015-03-21 13:00:20 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEUInt8 , UInt8 , Flags );
2014-09-08 19:24:33 +02:00
if (( Flags & 0x2 ) != 0 )
{
m_Client -> HandleUnmount ();
}
else if (( Flags & 0x1 ) != 0 )
{
m_Client -> HandleSteerVehicle ( Forward , Sideways );
}
}
void cProtocol180 :: HandlePacketTabComplete ( cByteBuffer & a_ByteBuffer )
{
HANDLE_READ ( a_ByteBuffer , ReadVarUTF8String , AString , Text );
2015-03-21 13:00:20 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBool , bool , HasPosition );
2014-09-08 19:24:33 +02:00
if ( HasPosition )
{
HANDLE_READ ( a_ByteBuffer , ReadBEInt64 , Int64 , Position );
}
m_Client -> HandleTabCompletion ( Text );
}
void cProtocol180 :: HandlePacketUpdateSign ( cByteBuffer & a_ByteBuffer )
{
2014-09-11 20:06:28 +02:00
int BlockX , BlockY , BlockZ ;
2015-03-22 23:09:23 +01:00
if ( ! a_ByteBuffer . ReadPosition64 ( BlockX , BlockY , BlockZ ))
2014-09-11 20:06:28 +02:00
{
return ;
}
AString Lines [ 4 ];
for ( int i = 0 ; i < 4 ; i ++ )
{
HANDLE_READ ( a_ByteBuffer , ReadVarUTF8String , AString , Line );
Lines [ i ] = Line . substr ( 1 , Line . length () - 2 ); // Remove ""
}
m_Client -> HandleUpdateSign ( BlockX , BlockY , BlockZ , Lines [ 0 ], Lines [ 1 ], Lines [ 2 ], Lines [ 3 ]);
2014-09-08 19:24:33 +02:00
}
void cProtocol180 :: HandlePacketUseEntity ( cByteBuffer & a_ByteBuffer )
{
HANDLE_READ ( a_ByteBuffer , ReadVarInt , UInt32 , EntityID );
HANDLE_READ ( a_ByteBuffer , ReadVarInt , UInt32 , Type );
switch ( Type )
{
case 0 :
{
2015-03-21 13:00:20 +01:00
m_Client -> HandleUseEntity ( EntityID , false );
2014-09-08 19:24:33 +02:00
break ;
}
case 1 :
{
2015-03-21 13:00:20 +01:00
m_Client -> HandleUseEntity ( EntityID , true );
2014-09-08 19:24:33 +02:00
break ;
}
case 2 :
{
HANDLE_READ ( a_ByteBuffer , ReadBEFloat , float , TargetX );
HANDLE_READ ( a_ByteBuffer , ReadBEFloat , float , TargetY );
HANDLE_READ ( a_ByteBuffer , ReadBEFloat , float , TargetZ );
// TODO: Do anything
break ;
}
default :
{
ASSERT ( ! "Unhandled use entity type!" );
return ;
}
}
}
void cProtocol180 :: HandlePacketEnchantItem ( cByteBuffer & a_ByteBuffer )
{
2015-03-21 13:00:20 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEUInt8 , UInt8 , WindowID );
HANDLE_READ ( a_ByteBuffer , ReadBEUInt8 , UInt8 , Enchantment );
2014-09-08 19:24:33 +02:00
m_Client -> HandleEnchantItem ( WindowID , Enchantment );
}
void cProtocol180 :: HandlePacketWindowClick ( cByteBuffer & a_ByteBuffer )
{
2015-03-21 13:00:20 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEUInt8 , UInt8 , WindowID );
HANDLE_READ ( a_ByteBuffer , ReadBEInt16 , Int16 , SlotNum );
HANDLE_READ ( a_ByteBuffer , ReadBEUInt8 , UInt8 , Button );
HANDLE_READ ( a_ByteBuffer , ReadBEUInt16 , UInt16 , TransactionID );
HANDLE_READ ( a_ByteBuffer , ReadBEUInt8 , UInt8 , Mode );
2014-09-08 19:24:33 +02:00
cItem Item ;
2014-09-22 22:06:08 +02:00
ReadItem ( a_ByteBuffer , Item );
2014-09-08 19:24:33 +02:00
// Convert Button, Mode, SlotNum and HeldItem into eClickAction:
eClickAction Action ;
switch (( Mode << 8 ) | Button )
{
2015-03-21 13:00:20 +01:00
case 0x0000 : Action = ( SlotNum != SLOT_NUM_OUTSIDE ) ? caLeftClick : caLeftClickOutside ; break ;
case 0x0001 : Action = ( SlotNum != SLOT_NUM_OUTSIDE ) ? caRightClick : caRightClickOutside ; break ;
2014-09-08 19:24:33 +02:00
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 ;
2015-06-25 13:01:48 +02:00
case 0x0302 : Action = caMiddleClick ; break ;
2015-03-21 13:00:20 +01:00
case 0x0400 : Action = ( SlotNum == SLOT_NUM_OUTSIDE ) ? caLeftClickOutsideHoldNothing : caDropKey ; break ;
case 0x0401 : Action = ( SlotNum == SLOT_NUM_OUTSIDE ) ? caRightClickOutsideHoldNothing : caCtrlDropKey ; break ;
case 0x0500 : Action = ( SlotNum == SLOT_NUM_OUTSIDE ) ? caLeftPaintBegin : caUnknown ; break ;
case 0x0501 : Action = ( SlotNum != SLOT_NUM_OUTSIDE ) ? caLeftPaintProgress : caUnknown ; break ;
case 0x0502 : Action = ( SlotNum == SLOT_NUM_OUTSIDE ) ? caLeftPaintEnd : caUnknown ; break ;
case 0x0504 : Action = ( SlotNum == SLOT_NUM_OUTSIDE ) ? caRightPaintBegin : caUnknown ; break ;
case 0x0505 : Action = ( SlotNum != SLOT_NUM_OUTSIDE ) ? caRightPaintProgress : caUnknown ; break ;
case 0x0506 : Action = ( SlotNum == SLOT_NUM_OUTSIDE ) ? caRightPaintEnd : caUnknown ; break ;
2014-09-08 19:24:33 +02:00
case 0x0600 : Action = caDblClick ; break ;
default :
{
LOGWARNING ( "Unhandled window click mode / button combination: %d (0x%x)" , ( Mode << 8 ) | Button , ( Mode << 8 ) | Button );
Action = caUnknown ;
break ;
}
}
m_Client -> HandleWindowClick ( WindowID , SlotNum , Action , Item );
}
void cProtocol180 :: HandlePacketWindowClose ( cByteBuffer & a_ByteBuffer )
{
2015-03-21 13:00:20 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEUInt8 , UInt8 , WindowID );
2014-09-08 19:24:33 +02:00
m_Client -> HandleWindowClose ( WindowID );
2014-09-04 00:29:36 +02:00
}
2014-09-30 13:33:57 +02:00
void cProtocol180 :: HandleVanillaPluginMessage ( cByteBuffer & a_ByteBuffer , const AString & a_Channel )
{
if ( a_Channel == "MC|AdvCdm" )
{
2015-03-21 13:00:20 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEUInt8 , UInt8 , Mode )
2014-09-30 13:33:57 +02:00
switch ( Mode )
{
case 0x00 :
{
2015-03-21 13:00:20 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEInt32 , Int32 , BlockX );
HANDLE_READ ( a_ByteBuffer , ReadBEInt32 , Int32 , BlockY );
HANDLE_READ ( a_ByteBuffer , ReadBEInt32 , Int32 , BlockZ );
2014-09-30 13:33:57 +02:00
HANDLE_READ ( a_ByteBuffer , ReadVarUTF8String , AString , Command );
m_Client -> HandleCommandBlockBlockChange ( BlockX , BlockY , BlockZ , Command );
break ;
}
default :
{
2015-03-21 13:00:20 +01:00
m_Client -> SendChat ( Printf ( "Failure setting command block command; unhandled mode %u (0x%02x)" , Mode , Mode ), mtFailure );
2014-09-30 13:33:57 +02:00
LOG ( "Unhandled MC|AdvCdm packet mode." );
return ;
}
} // switch (Mode)
return ;
}
else if ( a_Channel == "MC|Brand" )
{
HANDLE_READ ( a_ByteBuffer , ReadVarUTF8String , AString , Brand );
m_Client -> SetClientBrand ( Brand );
// Send back our brand, including the length:
2015-09-25 10:14:17 +02:00
SendPluginMessage ( "MC|Brand" , " \x08C uberite" );
2014-09-30 13:33:57 +02:00
return ;
}
else if ( a_Channel == "MC|Beacon" )
{
2015-03-21 13:00:20 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEInt32 , Int32 , Effect1 );
HANDLE_READ ( a_ByteBuffer , ReadBEInt32 , Int32 , Effect2 );
2014-09-30 13:33:57 +02:00
m_Client -> HandleBeaconSelection ( Effect1 , Effect2 );
return ;
}
else if ( a_Channel == "MC|ItemName" )
{
HANDLE_READ ( a_ByteBuffer , ReadVarUTF8String , AString , ItemName );
m_Client -> HandleAnvilItemName ( ItemName );
return ;
}
else if ( a_Channel == "MC|TrSel" )
{
2015-03-21 13:00:20 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEInt32 , Int32 , SlotNum );
2014-09-30 13:33:57 +02:00
m_Client -> HandleNPCTrade ( SlotNum );
return ;
}
LOG ( "Unhandled vanilla plugin channel: \" %s \" ." , a_Channel . c_str ());
// Read the payload and send it through to the clienthandle:
AString Message ;
VERIFY ( a_ByteBuffer . ReadString ( Message , a_ByteBuffer . GetReadableSpace () - 1 ));
m_Client -> HandlePluginMessage ( a_Channel , Message );
}
2014-09-08 19:24:33 +02:00
void cProtocol180 :: SendData ( const char * a_Data , size_t a_Size )
2014-09-04 00:29:36 +02:00
{
2014-09-08 19:24:33 +02:00
if ( m_IsEncrypted )
2014-09-04 00:29:36 +02:00
{
2014-09-08 19:24:33 +02:00
Byte Encrypted [ 8192 ]; // Larger buffer, we may be sending lots of data (chunks)
while ( a_Size > 0 )
{
size_t NumBytes = ( a_Size > sizeof ( Encrypted )) ? sizeof ( Encrypted ) : a_Size ;
2015-07-29 09:04:03 -06:00
m_Encryptor . ProcessData ( Encrypted , reinterpret_cast < Byte *> ( const_cast < char *> ( a_Data )), NumBytes );
m_Client -> SendData ( reinterpret_cast < const char *> ( Encrypted ), NumBytes );
2014-09-08 19:24:33 +02:00
a_Size -= NumBytes ;
a_Data += NumBytes ;
}
2014-09-04 00:29:36 +02:00
}
2014-09-08 19:24:33 +02:00
else
2014-09-04 00:29:36 +02:00
{
2014-09-08 19:24:33 +02:00
m_Client -> SendData ( a_Data , a_Size );
2014-09-04 00:29:36 +02:00
}
}
2014-09-25 20:34:49 +02:00
bool cProtocol180 :: ReadItem ( cByteBuffer & a_ByteBuffer , cItem & a_Item , size_t a_KeepRemainingBytes )
2014-09-08 17:02:54 +02:00
{
2015-03-21 13:00:20 +01:00
HANDLE_PACKET_READ ( a_ByteBuffer , ReadBEInt16 , Int16 , ItemType );
2014-09-08 17:02:54 +02:00
if ( ItemType == - 1 )
{
// The item is empty, no more data follows
a_Item . Empty ();
return true ;
}
a_Item . m_ItemType = ItemType ;
2015-03-21 13:00:20 +01:00
HANDLE_PACKET_READ ( a_ByteBuffer , ReadBEInt8 , Int8 , ItemCount );
HANDLE_PACKET_READ ( a_ByteBuffer , ReadBEInt16 , Int16 , ItemDamage );
2014-09-08 17:02:54 +02:00
a_Item . m_ItemCount = ItemCount ;
a_Item . m_ItemDamage = ItemDamage ;
if ( ItemCount <= 0 )
{
a_Item . Empty ();
}
2014-09-22 21:18:13 +02:00
AString Metadata ;
2014-09-25 20:34:49 +02:00
if ( ! a_ByteBuffer . ReadString ( Metadata , a_ByteBuffer . GetReadableSpace () - a_KeepRemainingBytes - 1 ) || ( Metadata . size () == 0 ) || ( Metadata [ 0 ] == 0 ))
2014-09-08 17:02:54 +02:00
{
// No metadata
return true ;
}
2014-09-12 02:42:04 +02:00
2014-09-08 19:24:33 +02:00
ParseItemMetadata ( a_Item , Metadata );
2014-09-08 17:02:54 +02:00
return true ;
}
2014-09-08 19:24:33 +02:00
void cProtocol180 :: ParseItemMetadata ( cItem & a_Item , const AString & a_Metadata )
2014-09-08 17:02:54 +02:00
{
2014-09-08 19:24:33 +02:00
// Parse into NBT:
cParsedNBT NBT ( a_Metadata . data (), a_Metadata . size ());
if ( ! NBT . IsValid ())
2014-09-08 17:02:54 +02:00
{
2014-09-08 19:24:33 +02:00
AString HexDump ;
2015-04-20 08:33:35 +02:00
CreateHexDump ( HexDump , a_Metadata . data (), std :: max < size_t > ( a_Metadata . size (), 1024 ), 16 );
2014-09-08 19:24:33 +02:00
LOGWARNING ( "Cannot parse NBT item metadata: (" SIZE_T_FMT " bytes) \n %s" , a_Metadata . size (), HexDump . c_str ());
2014-09-08 17:02:54 +02:00
return ;
}
2014-09-08 19:24:33 +02:00
// Load enchantments and custom display names from the NBT data:
for ( int tag = NBT . GetFirstChild ( NBT . GetRoot ()); tag >= 0 ; tag = NBT . GetNextSibling ( tag ))
2014-09-04 03:22:35 +02:00
{
2014-09-08 19:24:33 +02:00
AString TagName = NBT . GetName ( tag );
switch ( NBT . GetType ( tag ))
2014-09-04 03:22:35 +02:00
{
2014-09-08 19:24:33 +02:00
case TAG_List :
{
if (( TagName == "ench" ) || ( TagName == "StoredEnchantments" )) // Enchantments tags
{
EnchantmentSerializer :: ParseFromNBT ( a_Item . m_Enchantments , NBT , tag );
}
break ;
}
case TAG_Compound :
{
if ( TagName == "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
{
2015-05-09 09:25:09 +02:00
AppendPrintf ( Lore , "%s`" , NBT . GetString ( loretag ). c_str ()); // Append the lore with a grave accent / backtick, used internally by MCS to display a new line in the client; don't forget to c_str ;)
2014-09-08 19:24:33 +02:00
}
a_Item . m_Lore = Lore ;
}
2015-07-13 18:15:37 -06:00
else if (( NBT . GetType ( displaytag ) == TAG_Int ) && ( NBT . GetName ( displaytag ) == "color" ))
{
a_Item . m_ItemColor . m_Color = static_cast < unsigned int > ( NBT . GetInt ( displaytag ));
}
2014-09-08 19:24:33 +02:00
}
}
else if (( TagName == "Fireworks" ) || ( TagName == "Explosion" ))
{
2015-07-29 09:04:03 -06:00
cFireworkItem :: ParseFromNBT ( a_Item . m_FireworkItem , NBT , tag , static_cast < ENUM_ITEM_ID > ( a_Item . m_ItemType ));
2014-09-08 19:24:33 +02:00
}
break ;
}
case TAG_Int :
{
if ( TagName == "RepairCost" )
{
a_Item . m_RepairCost = NBT . GetInt ( tag );
}
}
default : LOGD ( "Unimplemented NBT data when parsing!" ); break ;
2014-09-04 03:22:35 +02:00
}
}
}
2014-09-08 19:24:33 +02:00
void cProtocol180 :: StartEncryption ( const Byte * a_Key )
2014-09-04 00:29:36 +02:00
{
2014-09-08 19:24:33 +02:00
m_Encryptor . Init ( a_Key , a_Key );
m_Decryptor . Init ( a_Key , a_Key );
m_IsEncrypted = true ;
2014-09-04 00:29:36 +02:00
2014-09-08 19:24:33 +02:00
// Prepare the m_AuthServerID:
cSha1Checksum Checksum ;
2014-09-04 00:29:36 +02:00
cServer * Server = cRoot :: Get () -> GetServer ();
2014-09-08 19:24:33 +02:00
const AString & ServerID = Server -> GetServerID ();
2015-07-29 09:04:03 -06:00
Checksum . Update ( reinterpret_cast < const Byte *> ( ServerID . c_str ()), ServerID . length ());
2014-09-08 19:24:33 +02:00
Checksum . Update ( a_Key , 16 );
2015-07-29 09:04:03 -06:00
Checksum . Update ( reinterpret_cast < const Byte *> ( Server -> GetPublicKeyDER (). data ()), Server -> GetPublicKeyDER (). size ());
2014-09-08 19:24:33 +02:00
Byte Digest [ 20 ];
Checksum . Finalize ( Digest );
cSha1Checksum :: DigestToJava ( Digest , m_AuthServerID );
2014-09-04 00:29:36 +02:00
}
2015-03-21 13:00:20 +01:00
eBlockFace cProtocol180 :: FaceIntToBlockFace ( Int8 a_BlockFace )
{
// Normalize the blockface values returned from the protocol
// Anything known gets mapped 1:1, everything else returns BLOCK_FACE_NONE
switch ( a_BlockFace )
{
case BLOCK_FACE_XM : return BLOCK_FACE_XM ;
case BLOCK_FACE_XP : return BLOCK_FACE_XP ;
case BLOCK_FACE_YM : return BLOCK_FACE_YM ;
case BLOCK_FACE_YP : return BLOCK_FACE_YP ;
case BLOCK_FACE_ZM : return BLOCK_FACE_ZM ;
case BLOCK_FACE_ZP : return BLOCK_FACE_ZP ;
default : return BLOCK_FACE_NONE ;
}
}
2014-09-08 19:24:33 +02:00
////////////////////////////////////////////////////////////////////////////////
// cProtocol180::cPacketizer:
2015-03-22 19:46:08 +01:00
void cProtocol180 :: SendPacket ( cPacketizer & a_Pkt )
2014-09-04 03:22:35 +02:00
{
2015-03-22 19:46:08 +01:00
UInt32 PacketLen = static_cast < UInt32 > ( m_OutPacketBuffer . GetUsedSpace ());
2014-09-12 02:42:04 +02:00
AString PacketData , CompressedPacket ;
2015-03-22 19:46:08 +01:00
m_OutPacketBuffer . ReadAll ( PacketData );
m_OutPacketBuffer . CommitRead ();
2014-09-08 19:24:33 +02:00
2015-03-22 19:46:08 +01:00
if (( m_State == 3 ) && ( PacketLen >= 256 ))
2014-09-12 02:42:04 +02:00
{
2015-03-22 19:46:08 +01:00
// Compress the packet payload:
2014-09-12 02:42:04 +02:00
if ( ! cProtocol180 :: CompressPacket ( PacketData , CompressedPacket ))
{
return ;
}
}
2015-03-22 19:46:08 +01:00
else if ( m_State == 3 )
2014-09-04 03:22:35 +02:00
{
2015-03-22 19:46:08 +01:00
// The packet is not compressed, indicate this in the packet header:
2015-03-22 23:09:23 +01:00
m_OutPacketLenBuffer . WriteVarInt32 ( PacketLen + 1 );
m_OutPacketLenBuffer . WriteVarInt32 ( 0 );
2014-09-12 02:42:04 +02:00
AString LengthData ;
2015-03-22 19:46:08 +01:00
m_OutPacketLenBuffer . ReadAll ( LengthData );
SendData ( LengthData . data (), LengthData . size ());
2014-09-04 03:22:35 +02:00
}
2014-09-08 19:24:33 +02:00
else
2014-09-04 03:22:35 +02:00
{
2015-03-22 19:46:08 +01:00
// Compression doesn't apply to this state, send raw data:
2015-03-22 23:09:23 +01:00
m_OutPacketLenBuffer . WriteVarInt32 ( PacketLen );
2014-09-12 02:42:04 +02:00
AString LengthData ;
2015-03-22 19:46:08 +01:00
m_OutPacketLenBuffer . ReadAll ( LengthData );
SendData ( LengthData . data (), LengthData . size ());
2014-09-08 19:24:33 +02:00
}
2014-09-12 02:42:04 +02:00
2015-03-22 19:46:08 +01:00
// Send the packet's payload, either direct or compressed:
2014-09-12 02:42:04 +02:00
if ( CompressedPacket . empty ())
{
2015-03-22 19:46:08 +01:00
m_OutPacketLenBuffer . CommitRead ();
SendData ( PacketData . data (), PacketData . size ());
2014-09-12 02:42:04 +02:00
}
else
{
2015-03-22 19:46:08 +01:00
SendData ( CompressedPacket . data (), CompressedPacket . size ());
2014-09-12 02:42:04 +02:00
}
2014-09-08 19:24:33 +02:00
// Log the comm into logfile:
2015-03-22 19:46:08 +01:00
if ( g_ShouldLogCommOut && m_CommLogFile . IsOpen ())
2014-09-08 19:24:33 +02:00
{
AString Hex ;
2014-09-12 02:42:04 +02:00
ASSERT ( PacketData . size () > 0 );
2015-03-22 19:46:08 +01:00
CreateHexDump ( Hex , PacketData . data (), PacketData . size (), 16 );
m_CommLogFile . Printf ( "Outgoing packet: type %d (0x%x), length %u (0x%x), state %d. Payload (incl. type): \n %s \n " ,
a_Pkt . GetPacketType (), a_Pkt . GetPacketType (), PacketLen , PacketLen , m_State , Hex . c_str ()
2014-09-08 19:24:33 +02:00
);
2014-09-04 03:22:35 +02:00
}
}
2015-03-22 19:46:08 +01:00
void cProtocol180 :: WriteItem ( cPacketizer & a_Pkt , const cItem & a_Item )
2014-09-04 03:22:35 +02:00
{
2014-09-08 19:24:33 +02:00
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 ())
{
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEInt16 ( - 1 );
2014-09-08 19:24:33 +02:00
return ;
}
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEInt16 ( ItemType );
a_Pkt . WriteBEInt8 ( a_Item . m_ItemCount );
a_Pkt . WriteBEInt16 ( a_Item . m_ItemDamage );
2014-09-08 19:24:33 +02:00
2015-07-13 18:15:37 -06:00
if ( a_Item . m_Enchantments . IsEmpty () && a_Item . IsBothNameAndLoreEmpty () && ( a_Item . m_ItemType != E_ITEM_FIREWORK_ROCKET ) && ( a_Item . m_ItemType != E_ITEM_FIREWORK_STAR ) && ! a_Item . m_ItemColor . IsValid ())
2014-09-08 19:24:33 +02:00
{
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEInt8 ( 0 );
2014-09-08 19:24:33 +02:00
return ;
}
2014-09-04 03:22:35 +02:00
2015-07-13 18:15:37 -06:00
2014-09-08 19:24:33 +02:00
// Send the enchantments and custom names:
cFastNBTWriter Writer ;
if ( a_Item . m_RepairCost != 0 )
2014-09-04 03:22:35 +02:00
{
2014-09-08 19:24:33 +02:00
Writer . AddInt ( "RepairCost" , a_Item . m_RepairCost );
}
if ( ! a_Item . m_Enchantments . IsEmpty ())
{
const char * TagName = ( a_Item . m_ItemType == E_ITEM_BOOK ) ? "StoredEnchantments" : "ench" ;
EnchantmentSerializer :: WriteToNBTCompound ( a_Item . m_Enchantments , Writer , TagName );
}
2015-07-13 18:15:37 -06:00
if ( ! a_Item . IsBothNameAndLoreEmpty () || a_Item . m_ItemColor . IsValid ())
2014-09-08 19:24:33 +02:00
{
Writer . BeginCompound ( "display" );
2015-07-13 18:15:37 -06:00
if ( a_Item . m_ItemColor . IsValid ())
{
2015-07-29 09:04:03 -06:00
Writer . AddInt ( "color" , static_cast < Int32 > ( a_Item . m_ItemColor . m_Color ));
2015-07-13 18:15:37 -06:00
}
2014-09-08 19:24:33 +02:00
if ( ! a_Item . IsCustomNameEmpty ())
{
Writer . AddString ( "Name" , a_Item . m_CustomName . c_str ());
}
if ( ! a_Item . IsLoreEmpty ())
{
Writer . BeginList ( "Lore" , TAG_String );
AStringVector Decls = StringSplit ( a_Item . m_Lore , "`" );
for ( AStringVector :: const_iterator itr = Decls . begin (), end = Decls . end (); itr != end ; ++ itr )
{
if ( itr -> empty ())
{
// The decl is empty (two `s), ignore
continue ;
}
Writer . AddString ( "" , itr -> c_str ());
}
Writer . EndList ();
}
Writer . EndCompound ();
}
if (( a_Item . m_ItemType == E_ITEM_FIREWORK_ROCKET ) || ( a_Item . m_ItemType == E_ITEM_FIREWORK_STAR ))
{
2015-03-22 19:46:08 +01:00
cFireworkItem :: WriteToNBTCompound ( a_Item . m_FireworkItem , Writer , static_cast < ENUM_ITEM_ID > ( a_Item . m_ItemType ));
2014-09-04 03:22:35 +02:00
}
2014-09-08 19:24:33 +02:00
Writer . Finish ();
AString Result = Writer . GetResult ();
if ( Result . size () == 0 )
{
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEInt8 ( 0 );
2014-09-08 19:24:33 +02:00
return ;
}
2015-03-22 19:46:08 +01:00
a_Pkt . WriteBuf ( Result . data (), Result . size ());
2014-09-04 03:22:35 +02:00
}
2015-03-22 19:46:08 +01:00
void cProtocol180 :: WriteBlockEntity ( cPacketizer & a_Pkt , const cBlockEntity & a_BlockEntity )
2014-09-04 03:22:35 +02:00
{
2014-09-08 19:24:33 +02:00
cFastNBTWriter Writer ;
2014-09-04 03:22:35 +02:00
2014-09-08 19:24:33 +02:00
switch ( a_BlockEntity . GetBlockType ())
2014-09-04 03:22:35 +02:00
{
2014-09-08 19:24:33 +02:00
case E_BLOCK_BEACON :
2014-09-04 03:22:35 +02:00
{
2015-03-22 19:46:08 +01:00
auto & BeaconEntity = reinterpret_cast < const cBeaconEntity &> ( a_BlockEntity );
Writer . AddInt ( "x" , BeaconEntity . GetPosX ());
Writer . AddInt ( "y" , BeaconEntity . GetPosY ());
Writer . AddInt ( "z" , BeaconEntity . GetPosZ ());
Writer . AddInt ( "Primary" , BeaconEntity . GetPrimaryEffect ());
2014-09-08 19:24:33 +02:00
Writer . AddInt ( "Secondary" , BeaconEntity . GetSecondaryEffect ());
2015-03-22 19:46:08 +01:00
Writer . AddInt ( "Levels" , BeaconEntity . GetBeaconLevel ());
2014-09-08 19:24:33 +02:00
Writer . AddString ( "id" , "Beacon" ); // "Tile Entity ID" - MC wiki; vanilla server always seems to send this though
2014-09-04 03:22:35 +02:00
break ;
}
2015-03-22 19:46:08 +01:00
2014-09-08 19:24:33 +02:00
case E_BLOCK_COMMAND_BLOCK :
2014-09-04 03:22:35 +02:00
{
2015-03-22 19:46:08 +01:00
auto & CommandBlockEntity = reinterpret_cast < const cCommandBlockEntity &> ( a_BlockEntity );
2014-09-08 19:24:33 +02:00
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 ())
{
2015-03-22 19:46:08 +01:00
Writer . AddString ( "LastOutput" , Printf ( "{ \" text \" : \" %s \" }" , CommandBlockEntity . GetLastOutput (). c_str ()));
2014-09-08 19:24:33 +02:00
}
2014-09-04 03:22:35 +02:00
break ;
}
2015-03-22 19:46:08 +01:00
2014-09-08 19:24:33 +02:00
case E_BLOCK_HEAD :
2014-09-04 03:22:35 +02:00
{
2015-03-22 19:46:08 +01:00
auto & MobHeadEntity = reinterpret_cast < const cMobHeadEntity &> ( a_BlockEntity );
2014-09-08 19:24:33 +02:00
Writer . AddInt ( "x" , MobHeadEntity . GetPosX ());
Writer . AddInt ( "y" , MobHeadEntity . GetPosY ());
Writer . AddInt ( "z" , MobHeadEntity . GetPosZ ());
Writer . AddByte ( "SkullType" , MobHeadEntity . GetType () & 0xFF );
Writer . AddByte ( "Rot" , MobHeadEntity . GetRotation () & 0xFF );
Writer . AddString ( "ExtraType" , MobHeadEntity . GetOwner (). c_str ());
Writer . AddString ( "id" , "Skull" ); // "Tile Entity ID" - MC wiki; vanilla server always seems to send this though
2014-09-04 03:22:35 +02:00
break ;
}
2015-03-22 19:46:08 +01:00
2014-09-08 19:24:33 +02:00
case E_BLOCK_FLOWER_POT :
2014-09-04 03:22:35 +02:00
{
2015-03-22 19:46:08 +01:00
auto & FlowerPotEntity = reinterpret_cast < const cFlowerPotEntity &> ( a_BlockEntity );
2014-09-08 19:24:33 +02:00
Writer . AddInt ( "x" , FlowerPotEntity . GetPosX ());
Writer . AddInt ( "y" , FlowerPotEntity . GetPosY ());
Writer . AddInt ( "z" , FlowerPotEntity . GetPosZ ());
2015-07-29 09:04:03 -06:00
Writer . AddInt ( "Item" , static_cast < Int32 > ( FlowerPotEntity . GetItem (). m_ItemType ));
Writer . AddInt ( "Data" , static_cast < Int32 > ( FlowerPotEntity . GetItem (). m_ItemDamage ));
2014-09-08 19:24:33 +02:00
Writer . AddString ( "id" , "FlowerPot" ); // "Tile Entity ID" - MC wiki; vanilla server always seems to send this though
break ;
2014-09-04 03:22:35 +02:00
}
2015-03-22 19:46:08 +01:00
2014-11-18 15:33:41 +01:00
case E_BLOCK_MOB_SPAWNER :
{
2015-03-22 19:46:08 +01:00
auto & MobSpawnerEntity = reinterpret_cast < const cMobSpawnerEntity &> ( a_BlockEntity );
2014-11-18 15:33:41 +01:00
Writer . AddInt ( "x" , MobSpawnerEntity . GetPosX ());
Writer . AddInt ( "y" , MobSpawnerEntity . GetPosY ());
Writer . AddInt ( "z" , MobSpawnerEntity . GetPosZ ());
2014-11-29 15:20:44 +01:00
Writer . AddString ( "EntityId" , cMonster :: MobTypeToVanillaName ( MobSpawnerEntity . GetEntity ()));
2014-11-18 15:33:41 +01:00
Writer . AddShort ( "Delay" , MobSpawnerEntity . GetSpawnDelay ());
Writer . AddString ( "id" , "MobSpawner" );
break ;
}
2015-03-22 19:46:08 +01:00
default :
{
break ;
}
2014-09-04 03:22:35 +02:00
}
2014-09-08 19:24:33 +02:00
Writer . Finish ();
2015-03-22 19:46:08 +01:00
a_Pkt . WriteBuf ( Writer . GetResult (). data (), Writer . GetResult (). size ());
2014-09-04 03:22:35 +02:00
}
2015-03-22 19:46:08 +01:00
void cProtocol180 :: WriteEntityMetadata ( cPacketizer & a_Pkt , const cEntity & a_Entity )
2014-09-04 19:03:21 +02:00
{
2014-09-08 19:24:33 +02:00
// 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 ;
}
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0 ); // Byte(0) + index 0
a_Pkt . WriteBEUInt8 ( Flags );
2015-07-13 22:25:40 +02:00
2014-09-08 19:24:33 +02:00
switch ( a_Entity . GetEntityType ())
2014-09-08 17:02:54 +02:00
{
2014-09-08 19:24:33 +02:00
case cEntity :: etPlayer : break ; // TODO?
case cEntity :: etPickup :
{
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 (( 5 << 5 ) | 10 ); // Slot(5) + index 10
2015-03-22 19:46:08 +01:00
WriteItem ( a_Pkt , reinterpret_cast < const cPickup &> ( a_Entity ). GetItem ());
2014-09-08 19:24:33 +02:00
break ;
}
case cEntity :: etMinecart :
{
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0x51 );
2014-09-08 19:24:33 +02:00
// 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
*/
2015-03-22 19:46:08 +01:00
auto & Minecart = reinterpret_cast < const cMinecart &> ( a_Entity );
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEInt32 (((( a_Entity . GetMaxHealth () / 2 ) - ( a_Entity . GetHealth () - ( a_Entity . GetMaxHealth () / 2 ))) * Minecart . LastDamage ()) * 4 );
a_Pkt . WriteBEUInt8 ( 0x52 );
a_Pkt . WriteBEInt32 ( 1 ); // Shaking direction, doesn't seem to affect anything
a_Pkt . WriteBEUInt8 ( 0x73 );
a_Pkt . WriteBEFloat ( static_cast < float > ( Minecart . LastDamage () + 10 )); // Damage taken / shake effect multiplyer
2014-09-08 19:24:33 +02:00
2015-03-22 19:46:08 +01:00
if ( Minecart . GetPayload () == cMinecart :: mpNone )
2014-09-08 19:24:33 +02:00
{
2015-03-22 19:46:08 +01:00
auto & RideableMinecart = reinterpret_cast < const cRideableMinecart &> ( Minecart );
2014-09-08 19:24:33 +02:00
const cItem & MinecartContent = RideableMinecart . GetContent ();
if ( ! MinecartContent . IsEmpty ())
{
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0x54 );
2014-09-08 19:24:33 +02:00
int Content = MinecartContent . m_ItemType ;
Content |= MinecartContent . m_ItemDamage << 8 ;
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEInt32 ( Content );
a_Pkt . WriteBEUInt8 ( 0x55 );
a_Pkt . WriteBEInt32 ( RideableMinecart . GetBlockHeight ());
a_Pkt . WriteBEUInt8 ( 0x56 );
a_Pkt . WriteBEUInt8 ( 1 );
2014-09-08 19:24:33 +02:00
}
}
2015-03-22 19:46:08 +01:00
else if ( Minecart . GetPayload () == cMinecart :: mpFurnace )
2014-09-08 19:24:33 +02:00
{
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0x10 );
a_Pkt . WriteBEUInt8 ( reinterpret_cast < const cMinecartWithFurnace &> ( Minecart ). IsFueled () ? 1 : 0 );
2014-09-08 19:24:33 +02:00
}
break ;
2015-03-22 19:46:08 +01:00
} // case etMinecart
2014-09-08 19:24:33 +02:00
case cEntity :: etProjectile :
{
2015-03-22 19:46:08 +01:00
auto & Projectile = reinterpret_cast < const cProjectileEntity &> ( a_Entity );
2014-09-08 19:24:33 +02:00
switch ( Projectile . GetProjectileKind ())
{
case cProjectileEntity :: pkArrow :
{
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0x10 );
a_Pkt . WriteBEUInt8 ( reinterpret_cast < const cArrowEntity &> ( Projectile ). IsCritical () ? 1 : 0 );
2014-09-08 19:24:33 +02:00
break ;
}
case cProjectileEntity :: pkFirework :
{
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0xa8 );
2015-03-22 19:46:08 +01:00
WriteItem ( a_Pkt , reinterpret_cast < const cFireworkEntity &> ( Projectile ). GetItem ());
break ;
}
default :
{
2014-09-08 19:24:33 +02:00
break ;
}
}
break ;
2015-03-22 19:46:08 +01:00
} // case etProjectile
2014-09-08 19:24:33 +02:00
case cEntity :: etMonster :
{
2015-03-22 19:46:08 +01:00
WriteMobMetadata ( a_Pkt , reinterpret_cast < const cMonster &> ( a_Entity ));
2014-09-08 19:24:33 +02:00
break ;
}
2015-03-22 19:46:08 +01:00
2014-09-08 19:24:33 +02:00
case cEntity :: etItemFrame :
{
2015-03-22 19:46:08 +01:00
auto & Frame = reinterpret_cast < const cItemFrame &> ( a_Entity );
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0xa8 );
2015-03-22 19:46:08 +01:00
WriteItem ( a_Pkt , Frame . GetItem ());
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0x09 );
a_Pkt . WriteBEUInt8 ( Frame . GetItemRotation ());
2015-03-22 19:46:08 +01:00
break ;
} // case etItemFrame
default :
{
2014-09-08 19:24:33 +02:00
break ;
}
2014-09-08 17:02:54 +02:00
}
}
2015-03-22 19:46:08 +01:00
void cProtocol180 :: WriteMobMetadata ( cPacketizer & a_Pkt , const cMonster & a_Mob )
2014-09-08 17:02:54 +02:00
{
2015-07-13 22:25:40 +02:00
// Living Enitiy Metadata
if ( a_Mob . HasCustomName ())
{
a_Pkt . WriteBEUInt8 ( 0x82 );
a_Pkt . WriteString ( a_Mob . GetCustomName ());
a_Pkt . WriteBEUInt8 ( 0x03 );
a_Pkt . WriteBool ( a_Mob . IsCustomNameAlwaysVisible ());
}
a_Pkt . WriteBEUInt8 ( 0x66 );
2015-07-29 09:04:03 -06:00
a_Pkt . WriteBEFloat ( static_cast < float > ( a_Mob . GetHealth ()));
2015-07-13 22:25:40 +02:00
2014-09-08 19:24:33 +02:00
switch ( a_Mob . GetMobType ())
2014-09-08 17:02:54 +02:00
{
2014-09-25 15:22:08 +01:00
case mtBat :
2014-09-08 19:24:33 +02:00
{
2015-03-22 19:46:08 +01:00
auto & Bat = reinterpret_cast < const cBat &> ( a_Mob );
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0x10 );
a_Pkt . WriteBEUInt8 ( Bat . IsHanging () ? 1 : 0 );
2014-09-08 19:24:33 +02:00
break ;
2015-03-22 19:46:08 +01:00
} // case mtBat
2015-07-12 18:00:56 +02:00
2015-03-22 19:46:08 +01:00
case mtCreeper :
2014-09-08 19:24:33 +02:00
{
2015-03-22 19:46:08 +01:00
auto & Creeper = reinterpret_cast < const cCreeper &> ( a_Mob );
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0x10 );
2015-11-09 19:50:40 +01:00
a_Pkt . WriteBEUInt8 ( Creeper . IsBlowing () ? 1 : 255 );
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0x11 );
a_Pkt . WriteBEUInt8 ( Creeper . IsCharged () ? 1 : 0 );
2014-09-08 19:24:33 +02:00
break ;
2015-03-22 19:46:08 +01:00
} // case mtCreeper
2015-07-12 18:00:56 +02:00
2015-03-22 19:46:08 +01:00
case mtEnderman :
2014-09-08 19:24:33 +02:00
{
2015-03-22 19:46:08 +01:00
auto & Enderman = reinterpret_cast < const cEnderman &> ( a_Mob );
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0x30 );
a_Pkt . WriteBEInt16 ( static_cast < Byte > ( Enderman . GetCarriedBlock ()));
a_Pkt . WriteBEUInt8 ( 0x11 );
a_Pkt . WriteBEUInt8 ( static_cast < Byte > ( Enderman . GetCarriedMeta ()));
a_Pkt . WriteBEUInt8 ( 0x12 );
a_Pkt . WriteBEUInt8 ( Enderman . IsScreaming () ? 1 : 0 );
2014-09-08 19:24:33 +02:00
break ;
2015-03-22 19:46:08 +01:00
} // case mtEnderman
2015-07-12 18:00:56 +02:00
2014-09-25 15:22:08 +01:00
case mtGhast :
2014-09-08 19:24:33 +02:00
{
2015-03-22 19:46:08 +01:00
auto & Ghast = reinterpret_cast < const cGhast &> ( a_Mob );
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0x10 );
a_Pkt . WriteBEUInt8 ( Ghast . IsCharging ());
2014-09-08 19:24:33 +02:00
break ;
2015-03-22 19:46:08 +01:00
} // case mtGhast
2015-07-12 18:00:56 +02:00
2015-03-22 19:46:08 +01:00
case mtHorse :
2014-09-08 19:24:33 +02:00
{
2015-03-22 19:46:08 +01:00
auto & Horse = reinterpret_cast < const cHorse &> ( a_Mob );
int Flags = 0 ;
if ( Horse . IsTame ())
2014-09-08 19:24:33 +02:00
{
2015-03-22 19:46:08 +01:00
Flags |= 0x02 ;
2014-09-08 19:24:33 +02:00
}
2015-03-22 19:46:08 +01:00
if ( Horse . IsSaddled ())
2014-09-08 19:24:33 +02:00
{
2015-03-22 19:46:08 +01:00
Flags |= 0x04 ;
2014-09-08 19:24:33 +02:00
}
2015-03-22 19:46:08 +01:00
if ( Horse . IsChested ())
2014-09-08 19:24:33 +02:00
{
2015-03-22 19:46:08 +01:00
Flags |= 0x08 ;
2014-09-08 19:24:33 +02:00
}
2015-03-22 19:46:08 +01:00
if ( Horse . IsEating ())
{
Flags |= 0x20 ;
}
if ( Horse . IsRearing ())
{
Flags |= 0x40 ;
}
if ( Horse . IsMthOpen ())
{
Flags |= 0x80 ;
}
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0x50 ); // Int at index 16
a_Pkt . WriteBEInt32 ( Flags );
a_Pkt . WriteBEUInt8 ( 0x13 ); // Byte at index 19
2015-07-29 09:04:03 -06:00
a_Pkt . WriteBEUInt8 ( static_cast < UInt8 > ( Horse . GetHorseType ()));
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0x54 ); // Int at index 20
2015-03-22 19:46:08 +01:00
int Appearance = 0 ;
Appearance = Horse . GetHorseColor ();
Appearance |= Horse . GetHorseStyle () << 8 ;
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEInt32 ( Appearance );
a_Pkt . WriteBEUInt8 ( 0x56 ); // Int at index 22
a_Pkt . WriteBEInt32 ( Horse . GetHorseArmour ());
2015-07-12 18:00:56 +02:00
a_Pkt . WriteBEUInt8 ( 0x0c );
2015-08-24 23:05:15 +02:00
a_Pkt . WriteBEInt8 ( Horse . IsBaby () ? - 1 : 0 );
2014-09-08 19:24:33 +02:00
break ;
2015-03-22 19:46:08 +01:00
} // case mtHorse
case mtMagmaCube :
{
auto & MagmaCube = reinterpret_cast < const cMagmaCube &> ( a_Mob );
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0x10 );
2015-07-29 09:04:03 -06:00
a_Pkt . WriteBEUInt8 ( static_cast < UInt8 > ( MagmaCube . GetSize ()));
2015-03-22 19:46:08 +01:00
break ;
} // case mtMagmaCube
2015-07-12 18:00:56 +02:00
case mtOcelot :
{
auto & Ocelot = reinterpret_cast < const cOcelot &> ( a_Mob );
a_Pkt . WriteBEUInt8 ( 0x0c );
2015-08-24 23:05:15 +02:00
a_Pkt . WriteBEInt8 ( Ocelot . IsBaby () ? - 1 : 0 );
2015-07-12 18:00:56 +02:00
break ;
} // case mtOcelot
2015-03-22 19:46:08 +01:00
case mtPig :
{
auto & Pig = reinterpret_cast < const cPig &> ( a_Mob );
2015-07-12 18:00:56 +02:00
a_Pkt . WriteBEUInt8 ( 0x0c );
2015-08-24 23:05:15 +02:00
a_Pkt . WriteBEInt8 ( Pig . IsBaby () ? - 1 : 0 );
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0x10 );
a_Pkt . WriteBEUInt8 ( Pig . IsSaddled () ? 1 : 0 );
2015-03-22 19:46:08 +01:00
break ;
} // case mtPig
2015-07-12 18:00:56 +02:00
2014-09-25 15:22:08 +01:00
case mtSheep :
2014-09-08 19:24:33 +02:00
{
2015-03-22 19:46:08 +01:00
auto & Sheep = reinterpret_cast < const cSheep &> ( a_Mob );
2015-07-12 18:00:56 +02:00
a_Pkt . WriteBEUInt8 ( 0x0c );
2015-08-24 23:05:15 +02:00
a_Pkt . WriteBEInt8 ( Sheep . IsBaby () ? - 1 : 0 );
2015-07-12 18:00:56 +02:00
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0x10 );
2014-09-08 19:24:33 +02:00
Byte SheepMetadata = 0 ;
2015-07-29 09:04:03 -06:00
SheepMetadata = static_cast < Byte > ( Sheep . GetFurColor ());
2015-03-22 19:46:08 +01:00
if ( Sheep . IsSheared ())
2014-09-08 19:24:33 +02:00
{
SheepMetadata |= 0x10 ;
}
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( SheepMetadata );
2014-09-08 19:24:33 +02:00
break ;
2015-03-22 19:46:08 +01:00
} // case mtSheep
2015-07-12 18:00:56 +02:00
2015-07-17 01:09:06 +02:00
case mtRabbit :
{
auto & Rabbit = reinterpret_cast < const cRabbit &> ( a_Mob );
a_Pkt . WriteBEUInt8 ( 0x12 );
a_Pkt . WriteBEUInt8 ( Rabbit . GetRabbitTypeAsNumber ());
a_Pkt . WriteBEUInt8 ( 0x0c );
2015-08-24 23:05:15 +02:00
a_Pkt . WriteBEInt8 ( Rabbit . IsBaby () ? - 1 : 0 );
2015-07-17 01:09:06 +02:00
break ;
} // case mtRabbit
2014-09-25 15:22:08 +01:00
case mtSkeleton :
2014-09-08 19:24:33 +02:00
{
2015-03-22 19:46:08 +01:00
auto & Skeleton = reinterpret_cast < const cSkeleton &> ( a_Mob );
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0x0d );
a_Pkt . WriteBEUInt8 ( Skeleton . IsWither () ? 1 : 0 );
2014-09-08 19:24:33 +02:00
break ;
2015-03-22 19:46:08 +01:00
} // case mtSkeleton
2015-07-12 18:00:56 +02:00
2015-03-22 19:46:08 +01:00
case mtSlime :
2014-09-08 19:24:33 +02:00
{
2015-03-22 19:46:08 +01:00
auto & Slime = reinterpret_cast < const cSlime &> ( a_Mob );
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0x10 );
2015-07-29 09:04:03 -06:00
a_Pkt . WriteBEUInt8 ( static_cast < UInt8 > ( Slime . GetSize ()));
2014-09-08 19:24:33 +02:00
break ;
2015-03-22 19:46:08 +01:00
} // case mtSlime
2014-09-08 17:02:54 +02:00
2015-03-22 19:46:08 +01:00
case mtVillager :
2014-09-08 19:24:33 +02:00
{
2015-03-22 19:46:08 +01:00
auto & Villager = reinterpret_cast < const cVillager &> ( a_Mob );
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0x50 );
a_Pkt . WriteBEInt32 ( Villager . GetVilType ());
2015-07-12 18:00:56 +02:00
a_Pkt . WriteBEUInt8 ( 0x0c );
2015-08-24 23:05:15 +02:00
a_Pkt . WriteBEInt8 ( Villager . IsBaby () ? - 1 : 0 );
2014-09-08 19:24:33 +02:00
break ;
2015-03-22 19:46:08 +01:00
} // case mtVillager
2015-07-12 18:00:56 +02:00
2015-03-22 19:46:08 +01:00
case mtWitch :
2014-09-08 19:24:33 +02:00
{
2015-03-22 19:46:08 +01:00
auto & Witch = reinterpret_cast < const cWitch &> ( a_Mob );
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0x15 );
a_Pkt . WriteBEUInt8 ( Witch . IsAngry () ? 1 : 0 );
2014-09-08 19:24:33 +02:00
break ;
2015-03-22 19:46:08 +01:00
} // case mtWitch
case mtWither :
2014-09-08 19:24:33 +02:00
{
2015-03-22 19:46:08 +01:00
auto & Wither = reinterpret_cast < const cWither &> ( a_Mob );
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0x54 ); // Int at index 20
2015-07-29 09:04:03 -06:00
a_Pkt . WriteBEInt32 ( static_cast < Int32 > ( Wither . GetWitherInvulnerableTicks ()));
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0x66 ); // Float at index 6
a_Pkt . WriteBEFloat ( static_cast < float > ( a_Mob . GetHealth ()));
2014-09-08 19:24:33 +02:00
break ;
2015-03-22 19:46:08 +01:00
} // case mtWither
2015-07-12 18:00:56 +02:00
2015-03-22 19:46:08 +01:00
case mtWolf :
2014-09-08 19:24:33 +02:00
{
2015-03-22 19:46:08 +01:00
auto & Wolf = reinterpret_cast < const cWolf &> ( a_Mob );
Byte WolfStatus = 0 ;
if ( Wolf . IsSitting ())
2014-09-08 19:24:33 +02:00
{
2015-03-22 19:46:08 +01:00
WolfStatus |= 0x1 ;
2014-09-08 19:24:33 +02:00
}
2015-03-22 19:46:08 +01:00
if ( Wolf . IsAngry ())
2014-09-08 19:24:33 +02:00
{
2015-03-22 19:46:08 +01:00
WolfStatus |= 0x2 ;
2014-09-08 19:24:33 +02:00
}
2015-03-22 19:46:08 +01:00
if ( Wolf . IsTame ())
2014-09-08 19:24:33 +02:00
{
2015-03-22 19:46:08 +01:00
WolfStatus |= 0x4 ;
2014-09-08 19:24:33 +02:00
}
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0x10 );
a_Pkt . WriteBEUInt8 ( WolfStatus );
a_Pkt . WriteBEUInt8 ( 0x72 );
a_Pkt . WriteBEFloat ( static_cast < float > ( a_Mob . GetHealth ()));
a_Pkt . WriteBEUInt8 ( 0x13 );
a_Pkt . WriteBEUInt8 ( Wolf . IsBegging () ? 1 : 0 );
a_Pkt . WriteBEUInt8 ( 0x14 );
2015-07-29 09:04:03 -06:00
a_Pkt . WriteBEUInt8 ( static_cast < UInt8 > ( Wolf . GetCollarColor ()));
2015-07-12 18:00:56 +02:00
a_Pkt . WriteBEUInt8 ( 0x0c );
2015-08-24 23:05:15 +02:00
a_Pkt . WriteBEInt8 ( Wolf . IsBaby () ? - 1 : 0 );
2014-09-08 19:24:33 +02:00
break ;
2015-03-22 19:46:08 +01:00
} // case mtWolf
2015-07-12 18:00:56 +02:00
2015-03-22 19:46:08 +01:00
case mtZombie :
{
auto & Zombie = reinterpret_cast < const cZombie &> ( a_Mob );
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0x0c );
2015-07-12 18:00:56 +02:00
a_Pkt . WriteBEInt8 ( Zombie . IsBaby () ? 1 : - 1 );
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0x0d );
a_Pkt . WriteBEUInt8 ( Zombie . IsVillagerZombie () ? 1 : 0 );
a_Pkt . WriteBEUInt8 ( 0x0e );
a_Pkt . WriteBEUInt8 ( Zombie . IsConverting () ? 1 : 0 );
2015-03-22 19:46:08 +01:00
break ;
} // case mtZombie
2015-07-12 18:00:56 +02:00
case mtZombiePigman :
{
auto & ZombiePigman = reinterpret_cast < const cZombiePigman &> ( a_Mob );
a_Pkt . WriteBEUInt8 ( 0x0c );
a_Pkt . WriteBEInt8 ( ZombiePigman . IsBaby () ? 1 : - 1 );
break ;
} // case mtZombiePigman
2014-09-08 19:24:33 +02:00
} // switch (a_Mob.GetType())
2014-09-08 17:02:54 +02:00
}
2014-09-08 17:08:28 +02:00
2015-03-22 19:46:08 +01:00
void cProtocol180 :: WriteEntityProperties ( cPacketizer & a_Pkt , const cEntity & a_Entity )
2014-09-08 17:08:28 +02:00
{
2014-09-08 19:24:33 +02:00
if ( ! a_Entity . IsMob ())
2014-09-08 17:08:28 +02:00
{
2014-09-08 19:24:33 +02:00
// No properties for anything else than mobs
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEInt32 ( 0 );
2014-09-08 19:24:33 +02:00
return ;
2014-09-08 17:08:28 +02:00
}
2014-09-08 19:24:33 +02:00
// const cMonster & Mob = (const cMonster &)a_Entity;
2014-09-08 17:08:28 +02:00
2014-09-08 19:24:33 +02:00
// TODO: Send properties and modifiers based on the mob type
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEInt32 ( 0 ); // NumProperties
2014-09-08 19:24:33 +02:00
}
2014-09-08 17:08:28 +02:00