2014-09-01 20:12:56 +02:00
2013-10-30 23:24:46 +01:00
// Protocol17x.cpp
/*
Implements the 1.7.x protocol classes:
- cProtocol172
- release 1.7.2 protocol (#4)
2014-09-04 00:29:36 +02:00
- cProtocol176
- release 1.7.6 protocol (#5)
2013-10-30 23:24:46 +01:00
*/
#include "Globals.h"
2014-02-15 23:16:44 +01:00
#include "json/json.h"
2013-10-30 23:24:46 +01:00
#include "Protocol17x.h"
2013-11-04 21:20:36 +01:00
#include "ChunkDataSerializer.h"
2014-05-11 14:57:06 +03:00
#include "PolarSSL++/Sha1Checksum.h"
2015-03-22 19:46:08 +01:00
#include "Packetizer.h"
2014-05-11 14:57:06 +03:00
2013-10-30 23:24:46 +01:00
#include "../ClientHandle.h"
#include "../Root.h"
#include "../Server.h"
2013-11-01 16:20:15 +01:00
#include "../World.h"
2014-05-11 14:57:06 +03:00
#include "../StringCompression.h"
#include "../CompositeChat.h"
#include "../Statistics.h"
2013-11-03 11:58:49 +01:00
#include "../WorldStorage/FastNBT.h"
2014-01-19 07:38:59 -08:00
#include "../WorldStorage/EnchantmentSerializer.h"
2014-05-11 14:57:06 +03:00
2013-11-25 20:04:01 +01:00
#include "../Entities/ExpOrb.h"
2013-11-10 20:48:12 +00:00
#include "../Entities/Minecart.h"
2013-11-04 21:20:36 +01:00
#include "../Entities/FallingBlock.h"
2014-02-18 00:16:03 +00:00
#include "../Entities/Painting.h"
2013-11-04 21:20:36 +01:00
#include "../Entities/Pickup.h"
#include "../Entities/Player.h"
2014-02-17 23:00:03 +00:00
#include "../Entities/ItemFrame.h"
2014-05-11 14:57:06 +03:00
#include "../Entities/ArrowEntity.h"
#include "../Entities/FireworkEntity.h"
2013-11-08 20:56:19 +01:00
#include "../Mobs/IncludeAllMonsters.h"
2013-11-06 12:16:44 +01:00
#include "../UI/Window.h"
2014-05-11 14:57:06 +03:00
2014-07-30 21:59:35 +02:00
#include "../BlockEntities/BeaconEntity.h"
2014-01-19 13:25:35 +00:00
#include "../BlockEntities/CommandBlockEntity.h"
2014-02-19 14:45:09 +01:00
#include "../BlockEntities/MobHeadEntity.h"
2014-09-19 23:00:54 +02:00
#include "../BlockEntities/MobSpawnerEntity.h"
2014-03-07 01:30:34 +01:00
#include "../BlockEntities/FlowerPotEntity.h"
2014-08-20 16:01:30 +02:00
#include "Bindings/PluginManager.h"
2013-10-31 23:48:43 +01:00
2015-03-21 13:00:20 +01:00
/** The slot number that the client uses to indicate "outside the window". */
static const Int16 SLOT_NUM_OUTSIDE = - 999 ;
2013-12-13 17:53:00 +01:00
#define HANDLE_READ(ByteBuf, Proc, Type, Var) \
2013-10-31 23:48:43 +01:00
Type Var; \
2014-08-11 15:33:20 +02:00
if (!ByteBuf.Proc(Var))\
{\
return;\
}
2013-10-30 23:24:46 +01:00
2013-12-13 17:53:00 +01:00
#define HANDLE_PACKET_READ(ByteBuf, Proc, Type, Var) \
2013-11-06 20:48:35 +01:00
Type Var; \
{ \
2013-12-13 17:53:00 +01:00
if (!ByteBuf.Proc(Var)) \
2013-11-06 20:48:35 +01:00
{ \
2013-12-13 17:53:00 +01:00
ByteBuf.CheckValid(); \
2013-11-06 20:48:35 +01:00
return false; \
} \
2013-12-13 17:53:00 +01:00
ByteBuf.CheckValid(); \
2013-11-06 20:48:35 +01:00
}
2014-01-28 23:53:07 +01:00
const int MAX_ENC_LEN = 512 ; // Maximum size of the encrypted message; should be 128, but who knows...
2014-01-24 23:03:48 +01:00
// fwd: main.cpp:
2014-01-26 17:54:18 +01:00
extern bool g_ShouldLogCommIn , g_ShouldLogCommOut ;
2014-01-24 23:03:48 +01:00
2014-07-17 22:15:34 +02:00
////////////////////////////////////////////////////////////////////////////////
2014-01-24 23:03:48 +01:00
// cProtocol172:
2013-10-30 23:24:46 +01:00
cProtocol172 :: cProtocol172 ( cClientHandle * a_Client , const AString & a_ServerAddress , UInt16 a_ServerPort , UInt32 a_State ) :
super ( a_Client ),
m_ServerAddress ( a_ServerAddress ),
m_ServerPort ( a_ServerPort ),
m_State ( a_State ),
m_ReceivedData ( 32 KiB ),
2014-06-08 21:58:08 +02:00
m_IsEncrypted ( false ),
m_LastSentDimension ( dimNotSet )
2013-10-30 23:24:46 +01:00
{
2014-09-17 09:38:06 +02: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 ;
2014-09-17 20:55:46 +02:00
if ( cRoot :: Get () -> GetServer () -> ShouldAllowBungeeCord () && SplitZeroTerminatedStrings ( a_ServerAddress , Params ) && ( Params . size () == 4 ))
2014-09-17 09:38:06 +02:00
{
2014-09-17 11:07:42 +02:00
LOGD ( "Player at %s connected via BungeeCord" , Params [ 1 ]. c_str ());
2014-09-17 09:38:06 +02:00
m_ServerAddress = Params [ 0 ];
2014-09-17 11:07:42 +02:00
m_Client -> SetIPString ( Params [ 1 ]);
m_Client -> SetUUID ( cMojangAPI :: MakeUUIDShort ( Params [ 2 ]));
m_Client -> SetProperties ( Params [ 3 ]);
2014-09-17 09:38:06 +02:00
}
2016-02-05 23:45:45 +02:00
2014-01-24 23:03:48 +01:00
// Create the comm log file, if so requested:
2014-01-26 17:54:18 +01:00
if ( g_ShouldLogCommIn || g_ShouldLogCommOut )
2014-01-24 23:03:48 +01:00
{
2014-04-14 20:21:00 +02:00
static int sCounter = 0 ;
2014-01-24 23:03:48 +01:00
cFile :: CreateFolder ( "CommLogs" );
2015-07-29 09:04:03 -06:00
AString FileName = Printf ( "CommLogs/%x_%d__%s.log" , static_cast < unsigned > ( time ( nullptr )), sCounter ++ , a_Client -> GetIPString (). c_str ());
2014-01-24 23:03:48 +01:00
m_CommLogFile . Open ( FileName , cFile :: fmWrite );
}
2013-10-30 23:24:46 +01:00
}
2014-03-07 11:04:25 -08:00
void cProtocol172 :: DataReceived ( const char * a_Data , size_t a_Size )
2013-10-30 23:24:46 +01:00
{
if ( m_IsEncrypted )
{
2014-01-23 23:35:23 +01:00
Byte Decrypted [ 512 ];
2013-10-30 23:24:46 +01:00
while ( a_Size > 0 )
{
2014-02-05 13:54:47 +01:00
size_t NumBytes = ( a_Size > sizeof ( Decrypted )) ? sizeof ( Decrypted ) : a_Size ;
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 );
2013-10-30 23:24:46 +01:00
a_Size -= NumBytes ;
a_Data += NumBytes ;
}
}
else
{
AddReceivedData ( a_Data , a_Size );
}
}
2013-11-01 16:20:15 +01:00
2016-05-14 12:12:42 -07:00
void cProtocol172 :: SendAttachEntity ( const cEntity & a_Entity , const cEntity & a_Vehicle )
2013-11-01 16:20:15 +01:00
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x1b ); // Attach Entity packet
2015-03-22 23:09:23 +01:00
Pkt . WriteBEUInt32 ( a_Entity . GetUniqueID ());
2016-05-14 12:12:42 -07:00
Pkt . WriteBEUInt32 ( a_Vehicle . GetUniqueID ());
2013-11-03 11:58:49 +01:00
Pkt . WriteBool ( false );
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendBlockAction ( int a_BlockX , int a_BlockY , int a_BlockZ , char a_Byte1 , char a_Byte2 , BLOCKTYPE a_BlockType )
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x24 ); // Block Action packet
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt32 ( a_BlockX );
Pkt . WriteBEInt16 ( static_cast < Int16 > ( a_BlockY ));
Pkt . WriteBEInt32 ( a_BlockZ );
Pkt . WriteBEInt8 ( a_Byte1 );
Pkt . WriteBEInt8 ( a_Byte2 );
Pkt . WriteVarInt32 ( a_BlockType );
2013-11-01 16:20:15 +01:00
}
2015-03-21 16:11:57 +01:00
void cProtocol172 :: SendBlockBreakAnim ( UInt32 a_EntityID , int a_BlockX , int a_BlockY , int a_BlockZ , char a_Stage )
2013-11-01 16:20:15 +01:00
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2014-01-16 22:42:47 +00:00
cPacketizer Pkt ( * this , 0x25 ); // Block Break Animation packet
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( a_EntityID );
Pkt . WriteBEInt32 ( a_BlockX );
Pkt . WriteBEInt32 ( a_BlockY );
Pkt . WriteBEInt32 ( a_BlockZ );
Pkt . WriteBEInt8 ( a_Stage );
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendBlockChange ( int a_BlockX , int a_BlockY , int a_BlockZ , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta )
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2015-01-18 22:43:35 +01:00
ASSERT (( a_BlockY >= 0 ) && ( a_BlockY < 256 ));
2016-02-05 23:45:45 +02:00
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x23 ); // Block Change packet
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt32 ( a_BlockX );
Pkt . WriteBEUInt8 ( static_cast < UInt8 > ( a_BlockY ));
Pkt . WriteBEInt32 ( a_BlockZ );
Pkt . WriteVarInt32 ( a_BlockType );
Pkt . WriteBEUInt8 ( a_BlockMeta );
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendBlockChanges ( int a_ChunkX , int a_ChunkZ , const sSetBlockVector & a_Changes )
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2013-11-03 11:58:49 +01: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 );
Pkt . WriteBEUInt16 ( static_cast < UInt16 > ( a_Changes . size ()));
Pkt . WriteBEUInt32 ( static_cast < UInt32 > ( a_Changes . size () * 4 ));
2013-11-01 16:20:15 +01:00
for ( sSetBlockVector :: const_iterator itr = a_Changes . begin (), end = a_Changes . end (); itr != end ; ++ itr )
{
2015-01-18 22:43:35 +01:00
int Coords = itr -> m_RelY | ( itr -> m_RelZ << 8 ) | ( itr -> m_RelX << 12 );
int Blocks = static_cast < int > ( itr -> m_BlockMeta | ( itr -> m_BlockType << 4 ));
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt32 (( Coords << 16 ) | Blocks );
2013-11-01 16:20:15 +01:00
} // for itr - a_Changes[]
}
2015-09-20 23:07:53 +01:00
void cProtocol172 :: SendChat ( const AString & a_Message , eChatType a_Type )
2013-11-01 16:20:15 +01:00
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2015-06-03 18:11:12 +02:00
2015-09-20 23:07:53 +01:00
if ( a_Type != ctChatBox ) // 1.7.2 doesn't support anything else
2015-06-03 18:11:12 +02:00
{
return ;
}
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x02 ); // Chat Message packet
Pkt . WriteString ( Printf ( "{ \" text \" : \" %s \" }" , EscapeString ( a_Message ). c_str ()));
2013-11-01 16:20:15 +01:00
}
2015-09-20 23:07:53 +01:00
void cProtocol172 :: SendChat ( const cCompositeChat & a_Message , eChatType a_Type , bool a_ShouldUseChatPrefixes )
2014-02-15 23:16:44 +01:00
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2014-09-13 20:27:10 +02:00
2015-09-20 23:07:53 +01:00
if ( a_Type != ctChatBox ) // 1.7.2 doesn't support anything else
2015-06-03 18:11:12 +02:00
{
return ;
}
2014-02-15 23:16:44 +01: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 ));
2014-02-15 23:16:44 +01:00
}
2013-11-01 16:20:15 +01:00
void cProtocol172 :: SendChunkData ( int a_ChunkX , int a_ChunkZ , cChunkDataSerializer & a_Serializer )
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2013-11-04 21:20:36 +01:00
// Serialize first, before creating the Packetizer (the packetizer locks a CS)
// This contains the flags and bitmasks, too
2014-09-08 00:36:30 +02:00
const AString & ChunkData = a_Serializer . Serialize ( cChunkDataSerializer :: RELEASE_1_3_2 , a_ChunkX , a_ChunkZ );
2016-02-05 23:45:45 +02:00
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x21 ); // Chunk Data packet
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt32 ( a_ChunkX );
Pkt . WriteBEInt32 ( a_ChunkZ );
2013-11-03 11:58:49 +01:00
Pkt . WriteBuf ( ChunkData . data (), ChunkData . size ());
2013-11-01 16:20:15 +01:00
}
2014-06-27 20:56:29 +02:00
void cProtocol172 :: SendCollectEntity ( const cEntity & a_Entity , const cPlayer & a_Player )
2013-11-01 16:20:15 +01:00
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x0d ); // Collect Item packet
2015-03-22 23:09:23 +01:00
Pkt . WriteBEUInt32 ( a_Entity . GetUniqueID ());
Pkt . WriteBEUInt32 ( a_Player . GetUniqueID ());
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendDestroyEntity ( const cEntity & a_Entity )
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x13 ); // Destroy Entities packet
2015-03-22 23:09:23 +01:00
Pkt . WriteBEUInt8 ( 1 );
Pkt . WriteBEUInt32 ( a_Entity . GetUniqueID ());
2013-11-01 16:20:15 +01:00
}
2016-05-14 12:12:42 -07:00
void cProtocol172 :: SendDetachEntity ( const cEntity & a_Entity , const cEntity & a_PreviousVehicle )
{
ASSERT ( m_State == 3 ); // In game mode?
cPacketizer Pkt ( * this , 0x1b ); // Attach Entity packet
Pkt . WriteBEUInt32 ( a_Entity . GetUniqueID ());
Pkt . WriteBEUInt32 ( 0 );
Pkt . WriteBool ( false );
}
2013-11-01 16:20:15 +01:00
void cProtocol172 :: SendDisconnect ( const AString & a_Reason )
{
2014-01-13 20:32:15 +01:00
switch ( m_State )
{
case 2 :
{
// During login:
cPacketizer Pkt ( * this , 0 );
Pkt . WriteString ( Printf ( "{ \" text \" : \" %s \" }" , EscapeString ( a_Reason ). c_str ()));
break ;
}
case 3 :
{
// In-game:
cPacketizer Pkt ( * this , 0x40 );
Pkt . WriteString ( Printf ( "{ \" text \" : \" %s \" }" , EscapeString ( a_Reason ). c_str ()));
break ;
}
}
2013-11-01 16:20:15 +01:00
}
2013-11-03 11:58:49 +01:00
void cProtocol172 :: SendEditSign ( int a_BlockX , int a_BlockY , int a_BlockZ )
2013-11-01 16:20:15 +01:00
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x36 ); // Sign Editor Open packet
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt32 ( a_BlockX );
Pkt . WriteBEInt32 ( a_BlockY );
Pkt . WriteBEInt32 ( a_BlockZ );
2013-11-01 16:20:15 +01:00
}
2013-12-14 18:03:00 +01:00
void cProtocol172 :: SendEntityEffect ( const cEntity & a_Entity , int a_EffectID , int a_Amplifier , short a_Duration )
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2015-01-18 22:43:35 +01:00
ASSERT (( a_EffectID >= 0 ) && ( a_EffectID < 256 ));
ASSERT (( a_Amplifier >= 0 ) && ( a_Amplifier < 256 ));
2016-02-05 23:45:45 +02:00
2013-12-14 18:03:00 +01:00
cPacketizer Pkt ( * this , 0x1D ); // Entity Effect packet
2015-03-22 23:09:23 +01:00
Pkt . WriteBEUInt32 ( a_Entity . GetUniqueID ());
Pkt . WriteBEUInt8 ( static_cast < Byte > ( a_EffectID ));
Pkt . WriteBEUInt8 ( static_cast < Byte > ( a_Amplifier ));
Pkt . WriteBEInt16 ( a_Duration );
2013-12-14 18:03:00 +01:00
}
2013-11-01 16:20:15 +01:00
void cProtocol172 :: SendEntityEquipment ( const cEntity & a_Entity , short a_SlotNum , const cItem & a_Item )
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x04 ); // Entity Equipment packet
2015-03-22 23:09:23 +01:00
Pkt . WriteBEUInt32 ( a_Entity . GetUniqueID ());
Pkt . WriteBEInt16 ( a_SlotNum );
2015-03-22 19:46:08 +01:00
WriteItem ( Pkt , a_Item );
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendEntityHeadLook ( const cEntity & a_Entity )
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x19 ); // Entity Head Look packet
2015-03-22 23:09:23 +01:00
Pkt . WriteBEUInt32 ( a_Entity . GetUniqueID ());
2013-11-03 11:58:49 +01:00
Pkt . WriteByteAngle ( a_Entity . GetHeadYaw ());
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendEntityLook ( const cEntity & a_Entity )
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x16 ); // Entity Look packet
2015-03-22 23:09:23 +01:00
Pkt . WriteBEUInt32 ( a_Entity . GetUniqueID ());
2013-11-03 11:58:49 +01:00
Pkt . WriteByteAngle ( a_Entity . GetYaw ());
Pkt . WriteByteAngle ( a_Entity . GetPitch ());
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendEntityMetadata ( const cEntity & a_Entity )
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x1c ); // Entity Metadata packet
2015-03-22 23:09:23 +01:00
Pkt . WriteBEUInt32 ( 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
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendEntityProperties ( const cEntity & a_Entity )
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x20 ); // Entity Properties packet
2015-03-22 23:09:23 +01:00
Pkt . WriteBEUInt32 ( a_Entity . GetUniqueID ());
2015-03-22 19:46:08 +01:00
WriteEntityProperties ( Pkt , a_Entity );
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendEntityRelMove ( const cEntity & a_Entity , char a_RelX , char a_RelY , char a_RelZ )
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x15 ); // Entity Relative Move packet
2015-03-22 23:09:23 +01:00
Pkt . WriteBEUInt32 ( a_Entity . GetUniqueID ());
Pkt . WriteBEInt8 ( a_RelX );
Pkt . WriteBEInt8 ( a_RelY );
Pkt . WriteBEInt8 ( a_RelZ );
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendEntityRelMoveLook ( const cEntity & a_Entity , char a_RelX , char a_RelY , char a_RelZ )
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x17 ); // Entity Look And Relative Move packet
2015-03-22 23:09:23 +01:00
Pkt . WriteBEUInt32 ( a_Entity . GetUniqueID ());
Pkt . WriteBEInt8 ( a_RelX );
Pkt . WriteBEInt8 ( a_RelY );
Pkt . WriteBEInt8 ( a_RelZ );
2013-11-03 11:58:49 +01:00
Pkt . WriteByteAngle ( a_Entity . GetYaw ());
Pkt . WriteByteAngle ( a_Entity . GetPitch ());
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendEntityStatus ( const cEntity & a_Entity , char a_Status )
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2013-11-03 11:58:49 +01: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 );
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendEntityVelocity ( const cEntity & a_Entity )
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x12 ); // Entity Velocity packet
2015-03-22 23:09:23 +01:00
Pkt . WriteBEUInt32 ( 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-03-22 23:09:23 +01:00
Pkt . WriteBEInt16 ( static_cast < short > ( a_Entity . GetSpeedX () * 400 ));
Pkt . WriteBEInt16 ( static_cast < short > ( a_Entity . GetSpeedY () * 400 ));
Pkt . WriteBEInt16 ( static_cast < short > ( a_Entity . GetSpeedZ () * 400 ));
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendExplosion ( double a_BlockX , double a_BlockY , double a_BlockZ , float a_Radius , const cVector3iArray & a_BlocksAffected , const Vector3d & a_PlayerMotion )
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2013-11-03 11:58:49 +01: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 ()));
2013-11-03 11:58:49 +01: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 ));
2013-11-03 11:58:49 +01: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 ));
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendGameMode ( eGameMode a_GameMode )
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2013-11-03 11:58:49 +01: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!
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendHealth ( void )
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x06 ); // Update Health packet
2014-04-18 21:09:44 +02:00
cPlayer * Player = m_Client -> GetPlayer ();
2015-03-22 23:09:23 +01:00
Pkt . WriteBEFloat ( static_cast < float > ( Player -> GetHealth ()));
Pkt . WriteBEInt16 ( static_cast < short > ( Player -> GetFoodLevel ()));
Pkt . WriteBEFloat ( static_cast < float > ( Player -> GetFoodSaturationLevel ()));
2013-11-01 16:20:15 +01:00
}
2015-04-08 00:41:19 +10:00
void cProtocol172 :: SendHideTitle ( void )
{
// Not implemented in this protocol version
}
2013-11-01 16:20:15 +01:00
void cProtocol172 :: SendInventorySlot ( char a_WindowID , short a_SlotNum , const cItem & a_Item )
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2013-11-03 11:58:49 +01: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 );
2013-11-01 16:20:15 +01:00
}
2015-07-29 09:04:03 -06:00
void cProtocol172 :: SendKeepAlive ( UInt32 a_PingID )
2013-11-01 16:20:15 +01:00
{
2014-04-15 23:40:06 +02:00
// Drop the packet if the protocol is not in the Game state yet (caused a client crash):
if ( m_State != 3 )
{
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 ;
}
2016-02-05 23:45:45 +02:00
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x00 ); // Keep Alive packet
2015-08-05 01:24:59 +03:00
Pkt . WriteBEUInt32 ( a_PingID );
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendLogin ( const cPlayer & a_Player , const cWorld & a_World )
{
2013-11-03 11:58:49 +01:00
// Send the Join Game packet:
{
2014-04-18 21:09:44 +02:00
cServer * Server = cRoot :: Get () -> GetServer ();
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x01 ); // Join Game packet
2015-03-22 23:09:23 +01:00
Pkt . WriteBEUInt32 ( a_Player . GetUniqueID ());
Pkt . WriteBEUInt8 ( static_cast < Byte > ( a_Player . GetEffectiveGameMode ()) | ( Server -> IsHardcore () ? 0x08 : 0 )); // Hardcore flag bit 4
Pkt . WriteBEInt8 ( static_cast < char > ( a_World . GetDimension ()));
Pkt . WriteBEUInt8 ( 2 ); // TODO: Difficulty (set to Normal)
Pkt . WriteBEUInt8 ( static_cast < Byte > ( std :: min ( Server -> GetMaxPlayers (), 60 )));
2013-11-03 11:58:49 +01:00
Pkt . WriteString ( "default" ); // Level type - wtf?
}
2014-06-08 21:58:08 +02:00
m_LastSentDimension = a_World . GetDimension ();
2016-02-05 23:45:45 +02:00
2013-11-01 16:20:15 +01:00
// Send the spawn position:
2013-11-03 11:58:49 +01:00
{
cPacketizer Pkt ( * this , 0x05 ); // Spawn Position packet
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt32 ( static_cast < int > ( a_World . GetSpawnX ()));
Pkt . WriteBEInt32 ( static_cast < int > ( a_World . GetSpawnY ()));
Pkt . WriteBEInt32 ( static_cast < int > ( a_World . GetSpawnZ ()));
2013-11-03 11:58:49 +01:00
}
2016-02-05 23:45:45 +02:00
2013-11-01 16:20:15 +01:00
// Send player abilities:
SendPlayerAbilities ();
}
2014-04-14 22:52:59 +02:00
void cProtocol172 :: SendLoginSuccess ( void )
{
ASSERT ( m_State == 2 ); // State: login?
2016-02-05 23:45:45 +02:00
2014-04-27 17:31:40 +02:00
{
cPacketizer Pkt ( * this , 0x02 ); // Login success packet
2014-07-30 13:52:51 +02:00
Pkt . WriteString ( cMojangAPI :: MakeUUIDDashed ( m_Client -> GetUUID ()));
2014-04-27 17:31:40 +02:00
Pkt . WriteString ( m_Client -> GetUsername ());
}
2014-04-14 22:52:59 +02:00
m_State = 3 ; // State = Game
}
2014-02-18 00:16:03 +00:00
void cProtocol172 :: SendPaintingSpawn ( const cPainting & a_Painting )
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2014-02-18 00:16:03 +00:00
cPacketizer Pkt ( * this , 0x10 ); // Spawn Painting packet
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( a_Painting . GetUniqueID ());
2014-02-18 00:16:03 +00:00
Pkt . WriteString ( a_Painting . GetName (). c_str ());
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt32 ( static_cast < int > ( a_Painting . GetPosX ()));
Pkt . WriteBEInt32 ( static_cast < int > ( a_Painting . GetPosY ()));
Pkt . WriteBEInt32 ( static_cast < int > ( a_Painting . GetPosZ ()));
Pkt . WriteBEInt32 ( a_Painting . GetProtocolFacing ());
2014-02-18 00:16:03 +00:00
}
2013-11-01 16:20:15 +01:00
2015-06-30 15:50:15 +01:00
void cProtocol172 :: SendMapData ( const cMap & a_Map , int a_DataStartX , int a_DataStartY )
2014-02-13 17:13:09 +02:00
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2014-02-13 17:13:09 +02:00
2015-06-30 15:50:15 +01:00
{
ASSERT ( a_Map . GetScale () < 256 );
2014-02-13 17:13:09 +02:00
2015-06-30 15:50:15 +01:00
cPacketizer Pkt ( * this , 0x34 );
Pkt . WriteVarInt32 ( static_cast < UInt32 > ( a_Map . GetID ()));
Pkt . WriteBEUInt16 ( 2 );
2014-02-13 17:13:09 +02:00
2015-06-30 15:50:15 +01:00
Pkt . WriteBEUInt8 ( 2 );
Pkt . WriteBEUInt8 ( static_cast < Byte > ( a_Map . GetScale ()));
}
2014-02-13 17:13:09 +02:00
2015-06-30 15:50:15 +01:00
{
ASSERT ( a_Map . GetData (). size () + 3 <= USHRT_MAX );
ASSERT (( a_DataStartX >= 0 ) && ( a_DataStartX < 256 ));
ASSERT (( a_DataStartY >= 0 ) && ( a_DataStartY < 256 ));
2014-02-13 17:13:09 +02:00
2015-06-30 15:50:15 +01:00
cPacketizer Pkt ( * this , 0x34 );
Pkt . WriteVarInt32 ( static_cast < UInt32 > ( a_Map . GetID ()));
Pkt . WriteBEUInt16 ( static_cast < UInt16 > ( 3 + a_Map . GetData (). size ()));
2014-02-13 17:13:09 +02:00
2015-06-30 15:50:15 +01:00
Pkt . WriteBEUInt8 ( 0 );
Pkt . WriteBEUInt8 ( static_cast < Byte > ( a_DataStartX ));
Pkt . WriteBEUInt8 ( static_cast < Byte > ( a_DataStartX ));
2014-02-18 20:50:08 +02:00
2015-06-30 15:50:15 +01:00
Pkt . WriteBuf ( reinterpret_cast < const char *> ( a_Map . GetData (). data ()), a_Map . GetData (). size ());
2014-02-18 20:50:08 +02:00
}
2015-06-30 15:50:15 +01:00
{
ASSERT ( 1 + 3 * a_Map . GetDecorators (). size () < USHRT_MAX );
2014-02-18 20:50:08 +02:00
2015-06-30 15:50:15 +01:00
cPacketizer Pkt ( * this , 0x34 );
Pkt . WriteVarInt32 ( static_cast < UInt32 > ( a_Map . GetID ()));
Pkt . WriteBEUInt16 ( static_cast < UInt16 > ( 1 + ( 3 * a_Map . GetDecorators (). size ())));
2014-02-18 20:50:08 +02:00
2015-06-30 15:50:15 +01:00
Pkt . WriteBEUInt8 ( 1 );
2014-02-18 20:50:08 +02:00
2015-06-30 15:50:15 +01:00
for ( const auto & Decorator : a_Map . GetDecorators ())
{
ASSERT ( Decorator . GetPixelX () < 256 );
ASSERT ( Decorator . GetPixelZ () < 256 );
Pkt . WriteBEUInt8 ( static_cast < Byte > (( Decorator . GetRot () << 4 ) | static_cast < int > ( Decorator . GetType ())));
Pkt . WriteBEUInt8 ( static_cast < Byte > ( Decorator . GetPixelX ()));
Pkt . WriteBEUInt8 ( static_cast < Byte > ( Decorator . GetPixelZ ()));
}
}
2014-02-13 17:13:09 +02:00
}
2013-11-01 16:20:15 +01:00
void cProtocol172 :: SendPickupSpawn ( const cPickup & a_Pickup )
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2013-11-03 11:58:49 +01: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
2013-11-03 11:58:49 +01: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
2013-11-03 11:58:49 +01:00
}
{
cPacketizer Pkt ( * this , 0x1c ); // Entity Metadata packet
2015-03-22 23:09:23 +01:00
Pkt . WriteBEUInt32 ( 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
2013-11-03 11:58:49 +01:00
}
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendPlayerAbilities ( void )
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x39 ); // Player Abilities packet
2013-11-01 16:20:15 +01:00
Byte Flags = 0 ;
2014-04-18 21:09:44 +02:00
cPlayer * Player = m_Client -> GetPlayer ();
if ( Player -> IsGameModeCreative ())
2013-11-01 16:20:15 +01:00
{
Flags |= 0x01 ;
2014-07-17 22:15:34 +02:00
Flags |= 0x08 ; // Godmode, used for creative
2013-11-01 16:20:15 +01:00
}
2014-04-18 21:09:44 +02:00
if ( Player -> IsFlying ())
2013-12-15 14:48:17 +01:00
{
Flags |= 0x02 ;
}
2014-04-18 21:09:44 +02:00
if ( Player -> CanFly ())
2013-12-15 14:48:17 +01:00
{
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 ()));
2013-11-01 16:20:15 +01:00
}
2013-12-06 23:47:07 +00:00
void cProtocol172 :: SendEntityAnimation ( const cEntity & a_Entity , char a_Animation )
2013-11-01 16:20:15 +01:00
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x0b ); // Animation packet
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( a_Entity . GetUniqueID ());
Pkt . WriteBEInt8 ( a_Animation );
2013-11-01 16:20:15 +01:00
}
2014-09-11 17:03:09 +02:00
void cProtocol172 :: SendParticleEffect ( const AString & a_ParticleName , float a_SrcX , float a_SrcY , float a_SrcZ , float a_OffsetX , float a_OffsetY , float a_OffsetZ , float a_ParticleData , int a_ParticleAmount )
2013-12-22 14:45:25 +01:00
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2013-12-22 14:45:25 +01:00
cPacketizer Pkt ( * this , 0x2A );
Pkt . WriteString ( a_ParticleName );
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 );
2013-12-22 14:45:25 +01:00
}
2015-03-21 17:17:26 +00:00
void cProtocol172 :: SendParticleEffect ( const AString & a_ParticleName , Vector3f a_Src , Vector3f a_Offset , float a_ParticleData , int a_ParticleAmount , std :: array < int , 2 > a_Data )
{
// 1.72 doesn't support extra data
this -> SendParticleEffect ( a_ParticleName , a_Src . x , a_Src . y , a_Src . z , a_Offset . x , a_Offset . y , a_Offset . z , a_ParticleData , a_ParticleAmount );
}
2014-09-18 18:50:17 +02:00
void cProtocol172 :: SendPlayerListAddPlayer ( const cPlayer & a_Player )
2013-11-01 16:20:15 +01:00
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2014-09-18 18:50:17 +02:00
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x38 ); // Playerlist Item packet
2014-09-23 14:39:49 +02:00
Pkt . WriteString ( a_Player . GetPlayerListName ());
2014-09-18 18:50:17 +02:00
Pkt . WriteBool ( true );
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt16 ( a_Player . GetClientHandle () -> GetPing ());
2014-09-18 18:50:17 +02:00
}
void cProtocol172 :: SendPlayerListRemovePlayer ( const cPlayer & a_Player )
{
ASSERT ( m_State == 3 ); // In game mode?
cPacketizer Pkt ( * this , 0x38 );
2014-09-23 14:39:49 +02:00
Pkt . WriteString ( a_Player . GetPlayerListName ());
2014-09-18 18:50:17 +02:00
Pkt . WriteBool ( false );
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt16 ( 0 );
2014-09-18 18:50:17 +02:00
}
void cProtocol172 :: SendPlayerListUpdateGameMode ( const cPlayer & a_Player )
{
// Not implemented in this protocol version
UNUSED ( a_Player );
}
void cProtocol172 :: SendPlayerListUpdatePing ( const cPlayer & a_Player )
{
// It is a simple add player packet in this protocol.
SendPlayerListAddPlayer ( a_Player );
}
2014-09-26 17:37:19 +02:00
void cProtocol172 :: SendPlayerListUpdateDisplayName ( const cPlayer & a_Player , const AString & a_CustomName )
2014-09-18 18:50:17 +02:00
{
2014-09-26 17:37:19 +02:00
// Not implemented in this protocol version
UNUSED ( a_Player );
UNUSED ( a_CustomName );
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendPlayerMaxSpeed ( void )
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2013-11-08 22:40:31 +01:00
cPacketizer Pkt ( * this , 0x20 ); // Entity Properties
2014-04-18 21:09:44 +02:00
cPlayer * Player = m_Client -> GetPlayer ();
2015-03-22 23:09:23 +01:00
Pkt . WriteBEUInt32 ( Player -> GetUniqueID ());
Pkt . WriteBEInt32 ( 1 ); // Count
2013-11-08 22:40:31 +01:00
Pkt . WriteString ( "generic.movementSpeed" );
2014-03-20 15:45:42 +01:00
// 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-04-18 21:09:44 +02:00
if ( Player -> IsSprinting ())
2013-11-08 22:40:31 +01:00
{
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt16 ( 1 ); // Modifier count
Pkt . WriteBEUInt64 ( 0x662a6b8dda3e4c1c );
Pkt . WriteBEUInt64 ( 0x881396ea6097278d ); // UUID of the modifier
Pkt . WriteBEDouble ( Player -> GetSprintingMaxSpeed () - Player -> GetNormalMaxSpeed ());
Pkt . WriteBEUInt8 ( 2 );
2013-11-08 22:40:31 +01:00
}
else
{
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt16 ( 0 ); // Modifier count
2013-11-08 22:40:31 +01:00
}
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendPlayerMoveLook ( void )
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x08 ); // Player Position And Look packet
2014-04-18 21:09:44 +02:00
cPlayer * Player = m_Client -> GetPlayer ();
2015-03-22 23:09:23 +01:00
Pkt . WriteBEDouble ( Player -> GetPosX ());
2016-02-05 23:45:45 +02:00
2013-12-17 19:56:17 +01:00
// Protocol docs say this is PosY, but #323 says this is eye-pos
// Moreover, the "+ 0.001" is there because otherwise the player falls through the block they were standing on.
2015-03-22 23:09:23 +01:00
Pkt . WriteBEDouble ( Player -> GetStance () + 0.001 );
2016-02-05 23:45:45 +02:00
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 ()));
2014-04-18 21:09:44 +02:00
Pkt . WriteBool ( Player -> IsOnGround ());
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendPlayerPosition ( void )
{
2013-11-03 11:58:49 +01:00
// There is no dedicated packet for this, send the whole thing:
SendPlayerMoveLook ();
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendPlayerSpawn ( const cPlayer & a_Player )
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2013-11-03 11:58:49 +01:00
// 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-07-30 13:52:51 +02:00
Pkt . WriteString ( cMojangAPI :: MakeUUIDDashed ( a_Player . GetClientHandle () -> GetUUID ()));
2014-09-02 19:12:35 +02:00
if ( a_Player . HasCustomName ())
{
Pkt . WriteString ( a_Player . GetCustomName ());
}
else
{
Pkt . WriteString ( a_Player . GetName ());
}
2013-11-03 11:58:49 +01:00
Pkt . WriteFPInt ( a_Player . GetPosX ());
Pkt . WriteFPInt ( a_Player . GetPosY ());
Pkt . WriteFPInt ( a_Player . GetPosZ ());
Pkt . WriteByteAngle ( a_Player . GetYaw ());
Pkt . WriteByteAngle ( a_Player . GetPitch ());
short ItemType = a_Player . GetEquippedItem (). IsEmpty () ? 0 : a_Player . GetEquippedItem (). m_ItemType ;
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 ( 0x7f ); // Metadata: end
2013-11-01 16:20:15 +01:00
}
2014-01-09 11:39:42 +01:00
void cProtocol172 :: SendPluginMessage ( const AString & a_Channel , const AString & a_Message )
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2015-03-22 23:09:23 +01:00
ASSERT ( a_Message . size () <= std :: numeric_limits < UInt16 >:: max ());
2016-02-05 23:45:45 +02:00
2014-01-09 14:24:57 +01:00
cPacketizer Pkt ( * this , 0x3f );
2014-01-09 11:39:42 +01:00
Pkt . WriteString ( a_Channel );
2015-03-22 23:09:23 +01:00
Pkt . WriteBEUInt16 ( static_cast < UInt16 > ( a_Message . size ()));
2014-01-09 11:39:42 +01:00
Pkt . WriteBuf ( a_Message . data (), a_Message . size ());
}
2013-12-14 18:03:00 +01:00
void cProtocol172 :: SendRemoveEntityEffect ( const cEntity & a_Entity , int a_EffectID )
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2015-01-18 22:43:35 +01:00
ASSERT (( a_EffectID >= 0 ) && ( a_EffectID < 256 ));
2016-02-05 23:45:45 +02:00
2014-04-16 18:10:48 +02:00
cPacketizer Pkt ( * this , 0x1e );
2015-03-22 23:09:23 +01:00
Pkt . WriteBEUInt32 ( a_Entity . GetUniqueID ());
Pkt . WriteBEUInt8 ( static_cast < Byte > ( a_EffectID ));
2013-12-14 18:03:00 +01:00
}
2015-04-08 00:41:19 +10:00
void cProtocol172 :: SendResetTitle ( void )
{
// Not implemented in this protocol version
}
2014-07-18 20:10:51 +01:00
void cProtocol172 :: SendRespawn ( eDimension a_Dimension , bool a_ShouldIgnoreDimensionChecks )
2013-11-01 16:20:15 +01:00
{
2014-07-18 20:12:27 +01:00
if (( m_LastSentDimension == a_Dimension ) && ! a_ShouldIgnoreDimensionChecks )
2014-06-08 21:58:08 +02:00
{
2014-06-29 22:44:01 +01:00
// 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)
2014-06-08 21:58:08 +02:00
return ;
}
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x07 ); // Respawn packet
2014-04-18 21:09:44 +02:00
cPlayer * Player = m_Client -> GetPlayer ();
2015-07-29 09:04:03 -06:00
Pkt . WriteBEInt32 ( static_cast < int > ( 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 ()));
2013-11-03 11:58:49 +01:00
Pkt . WriteString ( "default" );
2014-06-12 15:21:07 +01:00
m_LastSentDimension = a_Dimension ;
2013-11-01 16:20:15 +01:00
}
2013-11-16 02:23:50 +11:00
void cProtocol172 :: SendExperience ( void )
2013-11-15 08:35:02 +11:00
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2014-04-16 18:10:48 +02:00
cPacketizer Pkt ( * this , 0x1f ); // Experience Packet
2014-04-18 21:09:44 +02:00
cPlayer * Player = m_Client -> GetPlayer ();
2015-03-22 23:09:23 +01:00
Pkt . WriteBEFloat ( Player -> GetXpPercentage ());
2015-06-07 15:21:18 +02:00
Pkt . WriteBEInt16 ( static_cast < Int16 > ( std :: min < int > ( Player -> GetXpLevel (), std :: numeric_limits < Int16 >:: max ())));
Pkt . WriteBEInt16 ( static_cast < Int16 > ( std :: min < int > ( Player -> GetCurrentXp (), std :: numeric_limits < Int16 >:: max ())));
2013-11-15 08:35:02 +11:00
}
2013-11-25 21:43:43 +01:00
void cProtocol172 :: SendExperienceOrb ( const cExpOrb & a_ExpOrb )
2013-11-25 20:04:01 +01:00
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2015-01-18 22:43:35 +01:00
ASSERT (( a_ExpOrb . GetReward () >= 0 ) && ( a_ExpOrb . GetReward () < SHRT_MAX ));
2016-02-05 23:45:45 +02:00
2014-07-17 22:50:58 +02:00
cPacketizer Pkt ( * this , 0x11 );
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( a_ExpOrb . GetUniqueID ());
2014-09-12 16:41:23 +02:00
Pkt . WriteFPInt ( a_ExpOrb . GetPosX ());
Pkt . WriteFPInt ( a_ExpOrb . GetPosY ());
Pkt . WriteFPInt ( a_ExpOrb . GetPosZ ());
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt16 ( static_cast < short > ( a_ExpOrb . GetReward ()));
2013-11-25 20:04:01 +01:00
}
2014-01-21 15:58:17 +02:00
void cProtocol172 :: SendScoreboardObjective ( const AString & a_Name , const AString & a_DisplayName , Byte a_Mode )
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2014-04-16 18:10:48 +02:00
cPacketizer Pkt ( * this , 0x3b );
2014-01-21 15:58:17 +02:00
Pkt . WriteString ( a_Name );
Pkt . WriteString ( a_DisplayName );
2015-03-22 23:09:23 +01:00
Pkt . WriteBEUInt8 ( a_Mode );
2014-01-21 15:58:17 +02:00
}
void cProtocol172 :: SendScoreUpdate ( const AString & a_Objective , const AString & a_Player , cObjective :: Score a_Score , Byte a_Mode )
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2014-04-16 18:10:48 +02:00
cPacketizer Pkt ( * this , 0x3c );
2014-01-21 15:58:17 +02:00
Pkt . WriteString ( a_Player );
2015-03-22 23:09:23 +01:00
Pkt . WriteBEUInt8 ( a_Mode );
2014-01-21 15:58:17 +02:00
if ( a_Mode != 1 )
{
Pkt . WriteString ( a_Objective );
2015-07-29 09:04:03 -06:00
Pkt . WriteBEInt32 ( static_cast < int > ( a_Score ));
2014-01-21 15:58:17 +02:00
}
}
void cProtocol172 :: SendDisplayObjective ( const AString & a_Objective , cScoreboard :: eDisplaySlot a_Display )
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2014-04-16 18:10:48 +02:00
cPacketizer Pkt ( * this , 0x3d );
2015-03-22 23:09:23 +01:00
Pkt . WriteBEUInt8 ( static_cast < Byte > ( a_Display ));
2014-01-21 15:58:17 +02:00
Pkt . WriteString ( a_Objective );
}
2015-04-08 00:41:19 +10:00
void cProtocol172 :: SendSetSubTitle ( const cCompositeChat & a_SubTitle )
{
// Not implemented in this protocol version
}
void cProtocol172 :: SendSetRawSubTitle ( const AString & a_SubTitle )
{
// Not implemented in this protocol version
}
void cProtocol172 :: SendSetTitle ( const cCompositeChat & a_Title )
{
// Not implemented in this protocol version
}
void cProtocol172 :: SendSetRawTitle ( const AString & a_Title )
{
// Not implemented in this protocol version
}
2014-07-13 02:08:02 +02:00
void cProtocol172 :: SendSoundEffect ( const AString & a_SoundName , double a_X , double a_Y , double a_Z , float a_Volume , float a_Pitch )
2013-11-01 16:20:15 +01:00
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2014-07-13 02:08:02 +02:00
2013-11-04 21:20:36 +01:00
cPacketizer Pkt ( * this , 0x29 ); // Sound Effect packet
Pkt . WriteString ( a_SoundName );
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt32 ( static_cast < int > ( a_X * 8.0 ));
Pkt . WriteBEInt32 ( static_cast < int > ( a_Y * 8.0 ));
Pkt . WriteBEInt32 ( static_cast < int > ( a_Z * 8.0 ));
Pkt . WriteBEFloat ( a_Volume );
Pkt . WriteBEUInt8 ( static_cast < Byte > ( a_Pitch * 63 ));
2013-11-01 16:20:15 +01:00
}
2015-11-23 23:39:19 +00:00
void cProtocol172 :: SendSoundParticleEffect ( const EffectID a_EffectID , int a_SrcX , int a_SrcY , int a_SrcZ , int a_Data )
2013-11-01 16:20:15 +01:00
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2015-01-18 22:43:35 +01:00
ASSERT (( a_SrcY >= 0 ) && ( a_SrcY < 256 ));
2016-02-05 23:45:45 +02:00
2013-11-04 21:20:36 +01:00
cPacketizer Pkt ( * this , 0x28 ); // Effect packet
2015-11-23 23:39:19 +00:00
Pkt . WriteBEInt32 ( static_cast < int > ( a_EffectID ));
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt32 ( a_SrcX );
Pkt . WriteBEUInt8 ( static_cast < Byte > ( a_SrcY ));
Pkt . WriteBEInt32 ( a_SrcZ );
Pkt . WriteBEInt32 ( a_Data );
2013-11-04 21:20:36 +01:00
Pkt . WriteBool ( false );
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendSpawnFallingBlock ( const cFallingBlock & a_FallingBlock )
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2013-11-04 21:20:36 +01:00
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
2013-11-04 21:20:36 +01: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-03-22 23:09:23 +01:00
Pkt . WriteBEInt32 (( static_cast < int > ( a_FallingBlock . GetBlockType ()) | (( static_cast < int > ( a_FallingBlock . GetBlockMeta ()) << 16 ))));
Pkt . WriteBEInt16 ( static_cast < short > ( a_FallingBlock . GetSpeedX () * 400 ));
Pkt . WriteBEInt16 ( static_cast < short > ( a_FallingBlock . GetSpeedY () * 400 ));
Pkt . WriteBEInt16 ( static_cast < short > ( a_FallingBlock . GetSpeedZ () * 400 ));
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendSpawnMob ( const cMonster & a_Mob )
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2013-11-04 21:20:36 +01:00
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 ()));
2013-11-04 21:20:36 +01: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-03-22 23:09:23 +01:00
Pkt . WriteBEInt16 ( static_cast < short > ( a_Mob . GetSpeedX () * 400 ));
Pkt . WriteBEInt16 ( static_cast < short > ( a_Mob . GetSpeedY () * 400 ));
Pkt . WriteBEInt16 ( static_cast < short > ( 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
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendSpawnObject ( const cEntity & a_Entity , char a_ObjectType , int a_ObjectData , Byte a_Yaw , Byte a_Pitch )
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2013-11-04 21:20:36 +01:00
cPacketizer Pkt ( * this , 0xe ); // Spawn Object packet
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( a_Entity . GetUniqueID ());
Pkt . WriteBEInt8 ( a_ObjectType );
2013-11-04 21:20:36 +01:00
Pkt . WriteFPInt ( a_Entity . GetPosX ());
Pkt . WriteFPInt ( a_Entity . GetPosY ());
Pkt . WriteFPInt ( a_Entity . GetPosZ ());
Pkt . WriteByteAngle ( a_Entity . GetPitch ());
2014-02-17 23:00:03 +00:00
Pkt . WriteByteAngle ( a_Entity . GetYaw ());
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt32 ( a_ObjectData );
2013-11-04 21:20:36 +01:00
if ( a_ObjectData != 0 )
{
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt16 ( static_cast < short > ( a_Entity . GetSpeedX () * 400 ));
Pkt . WriteBEInt16 ( static_cast < short > ( a_Entity . GetSpeedY () * 400 ));
Pkt . WriteBEInt16 ( static_cast < short > ( a_Entity . GetSpeedZ () * 400 ));
2013-11-04 21:20:36 +01:00
}
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendSpawnVehicle ( const cEntity & a_Vehicle , char a_VehicleType , char a_VehicleSubType )
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2013-11-04 21:20:36 +01:00
cPacketizer Pkt ( * this , 0xe ); // Spawn Object packet
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( a_Vehicle . GetUniqueID ());
Pkt . WriteBEInt8 ( a_VehicleType );
2013-11-04 21:20:36 +01:00
Pkt . WriteFPInt ( a_Vehicle . GetPosX ());
Pkt . WriteFPInt ( a_Vehicle . GetPosY ());
Pkt . WriteFPInt ( a_Vehicle . GetPosZ ());
Pkt . WriteByteAngle ( a_Vehicle . GetPitch ());
2014-02-17 23:00:03 +00:00
Pkt . WriteByteAngle ( a_Vehicle . GetYaw ());
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt32 ( a_VehicleSubType );
2013-11-04 21:20:36 +01:00
if ( a_VehicleSubType != 0 )
{
2015-03-22 23:09:23 +01: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 ));
2013-11-04 21:20:36 +01:00
}
2013-11-01 16:20:15 +01:00
}
2014-05-11 14:57:06 +03:00
void cProtocol172 :: SendStatistics ( const cStatManager & a_Manager )
{
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2014-05-11 14:57:06 +03:00
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-05-11 14:57:06 +03: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-05-11 14:57:06 +03: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-05-11 14:57:06 +03:00
Pkt . WriteString ( StatName );
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( static_cast < UInt32 > ( Value ));
2014-05-11 14:57:06 +03:00
}
}
2013-11-01 16:20:15 +01:00
void cProtocol172 :: SendTabCompletionResults ( const AStringVector & a_Results )
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2013-12-08 13:37:10 +00:00
cPacketizer Pkt ( * this , 0x3a ); // Tab-Complete packet
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( static_cast < UInt32 > ( a_Results . size ()));
2013-12-08 13:37:10 +00:00
2013-11-04 21:20:36 +01:00
for ( AStringVector :: const_iterator itr = a_Results . begin (), end = a_Results . end (); itr != end ; ++ itr )
{
2013-12-08 13:37:10 +00:00
Pkt . WriteString ( * itr );
2013-11-04 21:20:36 +01:00
}
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendTeleportEntity ( const cEntity & a_Entity )
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2013-11-04 21:20:36 +01:00
cPacketizer Pkt ( * this , 0x18 );
2015-03-22 23:09:23 +01:00
Pkt . WriteBEUInt32 ( a_Entity . GetUniqueID ());
2013-11-04 21:20:36 +01: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 ());
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendThunderbolt ( int a_BlockX , int a_BlockY , int a_BlockZ )
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2013-11-04 21:20:36 +01:00
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
2013-11-04 21:20:36 +01:00
Pkt . WriteFPInt ( a_BlockX );
Pkt . WriteFPInt ( a_BlockY );
Pkt . WriteFPInt ( a_BlockZ );
2013-11-01 16:20:15 +01:00
}
2015-04-08 00:41:19 +10:00
void cProtocol172 :: SendTitleTimes ( int a_FadeInTicks , int a_DisplayTicks , int a_FadeOutTicks )
{
// Not implemented in this protocol version
}
2014-08-11 00:20:28 +02:00
void cProtocol172 :: SendTimeUpdate ( Int64 a_WorldAge , Int64 a_TimeOfDay , bool a_DoDaylightCycle )
2013-11-01 16:20:15 +01:00
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2014-08-11 00:20:28 +02:00
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 );
}
2016-02-05 23:45:45 +02:00
2013-11-04 21:20:36 +01:00
cPacketizer Pkt ( * this , 0x03 );
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt64 ( a_WorldAge );
Pkt . WriteBEInt64 ( a_TimeOfDay );
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendUnloadChunk ( int a_ChunkX , int a_ChunkZ )
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2013-11-05 18:37:39 +01:00
cPacketizer Pkt ( * this , 0x21 ); // Chunk Data packet
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt32 ( a_ChunkX );
Pkt . WriteBEInt32 ( a_ChunkZ );
2013-11-05 18:37:39 +01:00
Pkt . WriteBool ( true );
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt16 ( 0 ); // Primary bitmap
Pkt . WriteBEInt16 ( 0 ); // Add bitmap
Pkt . WriteBEInt32 ( 0 ); // Compressed data size
2013-11-01 16:20:15 +01:00
}
2014-01-19 19:42:25 +00:00
void cProtocol172 :: SendUpdateBlockEntity ( cBlockEntity & a_BlockEntity )
2014-01-19 00:54:38 +00:00
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2014-01-19 00:54:38 +00:00
cPacketizer Pkt ( * this , 0x35 ); // Update tile entity packet
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt32 ( a_BlockEntity . GetPosX ());
Pkt . WriteBEInt16 ( static_cast < short > ( a_BlockEntity . GetPosY ()));
Pkt . WriteBEInt32 ( a_BlockEntity . GetPosZ ());
2014-01-19 19:42:25 +00:00
Byte Action = 0 ;
switch ( a_BlockEntity . GetBlockType ())
{
2014-07-17 22:15:34 +02:00
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
2014-07-30 21:59:35 +02:00
case E_BLOCK_BEACON : Action = 3 ; break ; // Update beacon entity
2014-07-17 22:15:34 +02:00
case E_BLOCK_HEAD : Action = 4 ; break ; // Update Mobhead entity
case E_BLOCK_FLOWER_POT : Action = 5 ; break ; // Update flower pot
2014-01-19 19:42:25 +00:00
default : ASSERT ( ! "Unhandled or unimplemented BlockEntity update request!" ); break ;
}
2015-03-22 23:09:23 +01:00
Pkt . WriteBEUInt8 ( Action );
2014-01-19 00:54:38 +00:00
2015-03-22 19:46:08 +01:00
WriteBlockEntity ( Pkt , a_BlockEntity );
2014-01-19 00:54:38 +00:00
}
2013-11-01 16:20:15 +01:00
void cProtocol172 :: SendUpdateSign ( int a_BlockX , int a_BlockY , int a_BlockZ , const AString & a_Line1 , const AString & a_Line2 , const AString & a_Line3 , const AString & a_Line4 )
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2014-09-06 18:59:17 +02:00
ASSERT (( a_BlockY >= 0 ) && ( a_BlockY < cChunkDef :: Height ));
2016-02-05 23:45:45 +02:00
2013-11-05 18:37:39 +01:00
cPacketizer Pkt ( * this , 0x33 );
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt32 ( a_BlockX );
Pkt . WriteBEInt16 ( static_cast < Int16 > ( a_BlockY ));
Pkt . WriteBEInt32 ( a_BlockZ );
2014-01-29 17:59:49 +01:00
// Need to send only up to 15 chars, otherwise the client crashes (#598)
Pkt . WriteString ( a_Line1 . substr ( 0 , 15 ));
Pkt . WriteString ( a_Line2 . substr ( 0 , 15 ));
Pkt . WriteString ( a_Line3 . substr ( 0 , 15 ));
Pkt . WriteString ( a_Line4 . substr ( 0 , 15 ));
2013-11-01 16:20:15 +01:00
}
2013-11-05 18:37:39 +01:00
void cProtocol172 :: SendUseBed ( const cEntity & a_Entity , int a_BlockX , int a_BlockY , int a_BlockZ )
2013-11-01 16:20:15 +01:00
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2015-03-21 15:40:56 +01:00
ASSERT (( a_BlockY >= 0 ) && ( a_BlockY < cChunkDef :: Height ));
2016-02-05 23:45:45 +02:00
2013-11-05 18:37:39 +01:00
cPacketizer Pkt ( * this , 0x0a );
2015-03-22 23:09:23 +01:00
Pkt . WriteBEUInt32 ( a_Entity . GetUniqueID ());
Pkt . WriteBEInt32 ( a_BlockX );
Pkt . WriteBEUInt8 ( static_cast < Byte > ( a_BlockY ));
Pkt . WriteBEInt32 ( a_BlockZ );
2013-11-01 16:20:15 +01:00
}
void cProtocol172 :: SendWeather ( eWeather a_Weather )
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2013-11-12 21:43:20 +00:00
{
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
2013-11-12 21:43:20 +00:00
}
// TODO: Fade effect, somehow
2013-11-01 16:20:15 +01:00
}
2013-11-06 12:16:44 +01:00
void cProtocol172 :: SendWholeInventory ( const cWindow & a_Window )
2013-11-01 16:20:15 +01:00
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2013-11-06 12:16:44 +01:00
cPacketizer Pkt ( * this , 0x30 ); // Window Items packet
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt8 ( a_Window . GetWindowID ());
Pkt . WriteBEInt16 ( static_cast < short > ( a_Window . GetNumSlots ()));
2013-11-06 12:16:44 +01: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 );
2013-11-06 12:16:44 +01:00
} // for itr - Slots[]
2013-11-01 16:20:15 +01:00
}
2013-11-06 12:16:44 +01:00
void cProtocol172 :: SendWindowClose ( const cWindow & a_Window )
2013-11-01 16:20:15 +01:00
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2013-11-06 12:16:44 +01:00
cPacketizer Pkt ( * this , 0x2e );
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt8 ( a_Window . GetWindowID ());
2013-11-01 16:20:15 +01:00
}
2013-11-08 21:32:14 +01:00
void cProtocol172 :: SendWindowOpen ( const cWindow & a_Window )
2013-11-01 16:20:15 +01:00
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2013-11-08 21:32:14 +01:00
if ( a_Window . GetWindowType () < 0 )
{
// Do not send this packet for player inventory windows
return ;
}
2016-02-05 23:45:45 +02:00
2013-11-06 12:16:44 +01:00
cPacketizer Pkt ( * this , 0x2d );
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt8 ( a_Window . GetWindowID ());
Pkt . WriteBEInt8 ( static_cast < char > ( a_Window . GetWindowType ()));
2013-11-08 21:32:14 +01:00
Pkt . WriteString ( a_Window . GetWindowTitle ());
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt8 ( static_cast < char > ( a_Window . GetNumNonInventorySlots ()));
2013-11-06 12:16:44 +01:00
Pkt . WriteBool ( true );
2013-11-08 21:32:14 +01:00
if ( a_Window . GetWindowType () == cWindow :: wtAnimalChest )
2013-11-06 12:16:44 +01:00
{
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt32 ( 0 ); // TODO: The animal's EntityID
2013-11-06 12:16:44 +01:00
}
2013-11-01 16:20:15 +01:00
}
2014-10-03 21:32:41 +01:00
void cProtocol172 :: SendWindowProperty ( const cWindow & a_Window , short a_Property , short a_Value )
2013-11-01 16:20:15 +01:00
{
2014-04-16 18:10:48 +02:00
ASSERT ( m_State == 3 ); // In game mode?
2016-02-05 23:45:45 +02:00
2013-11-06 12:16:44 +01:00
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 );
2013-11-01 16:20:15 +01:00
}
2014-04-04 11:47:46 +02:00
void cProtocol172 :: AddReceivedData ( const char * a_Data , size_t a_Size )
2013-10-30 23:24:46 +01:00
{
2014-01-24 23:03:48 +01:00
// Write the incoming data into the comm log file:
2014-01-26 17:54:18 +01:00
if ( g_ShouldLogCommIn )
2014-01-24 23:03:48 +01:00
{
2014-01-25 15:06:21 +01:00
if ( m_ReceivedData . GetReadableSpace () > 0 )
{
AString AllData ;
2014-03-08 08:33:38 -08:00
size_t OldReadableSpace = m_ReceivedData . GetReadableSpace ();
2014-01-25 15:06:21 +01:00
m_ReceivedData . ReadAll ( AllData );
m_ReceivedData . ResetRead ();
m_ReceivedData . SkipRead ( m_ReceivedData . GetReadableSpace () - OldReadableSpace );
2014-01-26 17:54:18 +01:00
ASSERT ( m_ReceivedData . GetReadableSpace () == OldReadableSpace );
2014-01-25 15:06:21 +01:00
AString Hex ;
CreateHexDump ( Hex , AllData . data (), AllData . size (), 16 );
2014-03-12 10:34:50 -07:00
m_CommLogFile . Printf ( "Incoming data, " SIZE_T_FMT " (0x" SIZE_T_FMT_HEX ") unparsed bytes already present in buffer: \n %s \n " ,
2014-01-25 15:06:21 +01:00
AllData . size (), AllData . size (), Hex . c_str ()
);
}
2014-01-24 23:03:48 +01:00
AString Hex ;
CreateHexDump ( Hex , a_Data , a_Size , 16 );
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-01-24 23:03:48 +01:00
);
2014-01-26 17:54:18 +01:00
m_CommLogFile . Flush ();
2014-01-24 23:03:48 +01:00
}
2014-09-08 12:24:06 +02:00
2013-10-30 23:24:46 +01:00
if ( ! m_ReceivedData . Write ( a_Data , a_Size ))
{
// Too much data in the incoming queue, report to caller:
m_Client -> PacketBufferFull ();
return ;
}
2014-09-08 12:24:06 +02:00
2013-10-30 23:24:46 +01:00
// Handle all complete packets:
2014-01-07 14:24:25 +01:00
for (;;)
2013-10-30 23:24:46 +01:00
{
UInt32 PacketLen ;
if ( ! m_ReceivedData . ReadVarInt ( PacketLen ))
{
// Not enough data
2014-01-26 17:54:18 +01:00
m_ReceivedData . ResetRead ();
break ;
2013-10-30 23:24:46 +01:00
}
if ( ! m_ReceivedData . CanReadBytes ( PacketLen ))
{
// The full packet hasn't been received yet
2014-01-26 17:54:18 +01:00
m_ReceivedData . ResetRead ();
break ;
2013-10-30 23:24:46 +01:00
}
2013-12-13 17:53:00 +01:00
cByteBuffer bb ( PacketLen + 1 );
2015-01-18 22:43:35 +01:00
VERIFY ( m_ReceivedData . ReadToByteBuffer ( bb , static_cast < size_t > ( PacketLen )));
2013-12-13 17:53:00 +01:00
m_ReceivedData . CommitRead ();
2014-09-08 12:24:06 +02:00
2013-10-30 23:24:46 +01:00
UInt32 PacketType ;
2013-12-13 17:53:00 +01:00
if ( ! bb . ReadVarInt ( PacketType ))
2013-10-30 23:24:46 +01:00
{
// Not enough data
2014-01-26 17:54:18 +01:00
break ;
2013-10-30 23:24:46 +01:00
}
2013-11-03 11:58:49 +01:00
2014-09-25 18:33:34 +02:00
// Write one NUL extra, so that we can detect over-reads
bb . Write ( " \0 " , 1 );
2016-02-05 23:45:45 +02:00
2014-01-24 23:03:48 +01:00
// Log the packet info into the comm log file:
2014-01-26 17:54:18 +01:00
if ( g_ShouldLogCommIn )
2014-01-24 23:03:48 +01:00
{
AString PacketData ;
bb . ReadAll ( PacketData );
bb . ResetRead ();
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:33:34 +02:00
ASSERT ( PacketData . size () > 0 ); // We have written an extra NUL, so there had to be at least one byte read
2014-01-24 23:03:48 +01: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 ()
);
}
2014-09-08 17:02:54 +02:00
2014-01-16 15:26:58 +01:00
if ( ! HandlePacket ( bb , PacketType ))
{
2014-01-16 20:39:59 +01:00
// Unknown packet, already been reported, but without the length. Log the length here:
2014-01-17 10:12:04 +01:00
LOGWARNING ( "Unhandled packet: type 0x%x, state %d, length %u" , PacketType , m_State , PacketLen );
2016-02-05 23:45:45 +02:00
2014-01-16 20:39:59 +01:00
#ifdef _DEBUG
// Dump the packet contents into the log:
bb . ResetRead ();
AString Packet ;
bb . ReadAll ( Packet );
Packet . resize ( Packet . size () - 1 ); // Drop the final NUL pushed there for over-read detection
AString Out ;
2015-03-21 15:40:56 +01:00
CreateHexDump ( Out , Packet . data (), Packet . size (), 24 );
2014-01-16 20:39:59 +01:00
LOGD ( "Packet contents: \n %s" , Out . c_str ());
#endif // _DEBUG
2016-02-05 23:45:45 +02:00
2014-01-25 15:28:16 +01:00
// Put a message in the comm log:
2014-01-26 17:54:18 +01:00
if ( g_ShouldLogCommIn )
2014-01-25 15:28:16 +01:00
{
m_CommLogFile . Printf ( "^^^^^^ Unhandled packet ^^^^^^ \n\n\n " );
}
2016-02-05 23:45:45 +02:00
2014-01-16 15:26:58 +01:00
return ;
}
2014-09-08 17:02:54 +02:00
2013-12-13 17:53:00 +01:00
if ( bb . GetReadableSpace () != 1 )
2013-10-31 23:48:43 +01:00
{
2013-12-13 17:53:00 +01:00
// Read more or less than packet length, report as error
2014-03-12 10:34:50 -07:00
LOGWARNING ( "Protocol 1.7: Wrong number of bytes read for packet 0x%x, state %d. Read " SIZE_T_FMT " bytes, packet contained %u bytes" ,
2014-01-17 10:12:04 +01:00
PacketType , m_State , bb . GetUsedSpace () - bb . GetReadableSpace (), PacketLen
2014-01-16 15:26:58 +01:00
);
2014-01-25 15:28:16 +01:00
// Put a message in the comm log:
2014-01-26 17:54:18 +01:00
if ( g_ShouldLogCommIn )
2014-01-25 15:28:16 +01:00
{
2014-03-12 10:34:50 -07:00
m_CommLogFile . Printf ( "^^^^^^ Wrong number of bytes read for this packet (exp %d left, got " SIZE_T_FMT " left) ^^^^^^ \n\n\n " ,
2014-01-25 15:28:16 +01:00
1 , bb . GetReadableSpace ()
);
m_CommLogFile . Flush ();
}
2013-12-13 17:53:00 +01:00
ASSERT ( ! "Read wrong number of bytes!" );
2013-10-31 23:48:43 +01:00
m_Client -> PacketError ( PacketType );
}
2014-07-20 23:10:31 +02:00
} // for (ever)
2014-09-08 17:02:54 +02:00
2014-01-26 17:54:18 +01:00
// Log any leftover bytes into the logfile:
if ( g_ShouldLogCommIn && ( m_ReceivedData . GetReadableSpace () > 0 ))
{
AString AllData ;
2014-03-08 08:33:38 -08:00
size_t OldReadableSpace = m_ReceivedData . GetReadableSpace ();
2014-01-26 17:54:18 +01:00
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 );
2014-03-12 10:34:50 -07:00
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" ,
2014-01-26 17:54:18 +01:00
m_ReceivedData . GetReadableSpace (), m_ReceivedData . GetReadableSpace (), Hex . c_str ()
);
m_CommLogFile . Flush ();
}
2013-10-30 23:24:46 +01:00
}
2014-01-16 15:26:58 +01:00
bool cProtocol172 :: HandlePacket ( cByteBuffer & a_ByteBuffer , UInt32 a_PacketType )
2013-10-30 23:24:46 +01:00
{
switch ( m_State )
{
case 1 :
{
// Status
switch ( a_PacketType )
{
2014-01-16 15:26:58 +01:00
case 0x00 : HandlePacketStatusRequest ( a_ByteBuffer ); return true ;
case 0x01 : HandlePacketStatusPing ( a_ByteBuffer ); return true ;
2013-10-30 23:24:46 +01:00
}
break ;
}
2016-02-05 23:45:45 +02:00
2013-10-30 23:24:46 +01:00
case 2 :
{
// Login
2013-10-31 23:48:43 +01:00
switch ( a_PacketType )
{
2014-01-16 15:26:58 +01:00
case 0x00 : HandlePacketLoginStart ( a_ByteBuffer ); return true ;
case 0x01 : HandlePacketLoginEncryptionResponse ( a_ByteBuffer ); return true ;
2013-10-31 23:48:43 +01:00
}
2013-10-30 23:24:46 +01:00
break ;
}
2016-02-05 23:45:45 +02:00
2013-10-30 23:24:46 +01:00
case 3 :
{
// Game
2013-10-31 23:48:43 +01:00
switch ( a_PacketType )
{
2014-01-16 15:26:58 +01:00
case 0x00 : HandlePacketKeepAlive ( a_ByteBuffer ); return true ;
case 0x01 : HandlePacketChatMessage ( a_ByteBuffer ); return true ;
case 0x02 : HandlePacketUseEntity ( a_ByteBuffer ); return true ;
case 0x03 : HandlePacketPlayer ( a_ByteBuffer ); return true ;
case 0x04 : HandlePacketPlayerPos ( a_ByteBuffer ); return true ;
case 0x05 : HandlePacketPlayerLook ( a_ByteBuffer ); return true ;
case 0x06 : HandlePacketPlayerPosLook ( a_ByteBuffer ); return true ;
case 0x07 : HandlePacketBlockDig ( a_ByteBuffer ); return true ;
case 0x08 : HandlePacketBlockPlace ( a_ByteBuffer ); return true ;
case 0x09 : HandlePacketSlotSelect ( a_ByteBuffer ); return true ;
case 0x0a : HandlePacketAnimation ( a_ByteBuffer ); return true ;
case 0x0b : HandlePacketEntityAction ( a_ByteBuffer ); return true ;
case 0x0c : HandlePacketSteerVehicle ( a_ByteBuffer ); return true ;
case 0x0d : HandlePacketWindowClose ( a_ByteBuffer ); return true ;
case 0x0e : HandlePacketWindowClick ( a_ByteBuffer ); return true ;
2014-07-17 22:15:34 +02:00
case 0x0f : // Confirm transaction - not used in MCS
2014-01-16 15:26:58 +01:00
case 0x10 : HandlePacketCreativeInventoryAction ( a_ByteBuffer ); return true ;
2014-01-31 19:46:51 +01:00
case 0x11 : HandlePacketEnchantItem ( a_ByteBuffer ); return true ;
2014-01-16 15:26:58 +01:00
case 0x12 : HandlePacketUpdateSign ( a_ByteBuffer ); return true ;
case 0x13 : HandlePacketPlayerAbilities ( a_ByteBuffer ); return true ;
case 0x14 : HandlePacketTabComplete ( a_ByteBuffer ); return true ;
case 0x15 : HandlePacketClientSettings ( a_ByteBuffer ); return true ;
case 0x16 : HandlePacketClientStatus ( a_ByteBuffer ); return true ;
case 0x17 : HandlePacketPluginMessage ( a_ByteBuffer ); return true ;
2013-10-31 23:48:43 +01:00
}
2013-10-30 23:24:46 +01:00
break ;
}
2014-01-17 10:12:04 +01:00
default :
{
// Received a packet in an unknown state, report:
LOGWARNING ( "Received a packet in an unknown protocol state %d. Ignoring further packets." , m_State );
2016-02-05 23:45:45 +02:00
2014-01-17 10:12:04 +01:00
// Cannot kick the client - we don't know this state and thus the packet number for the kick packet
2016-02-05 23:45:45 +02:00
2014-01-17 10:12:04 +01:00
// Switch to a state when all further packets are silently ignored:
m_State = 255 ;
return false ;
}
case 255 :
{
// This is the state used for "not processing packets anymore" when we receive a bad packet from a client.
// Do not output anything (the caller will do that for us), just return failure
return false ;
}
2013-10-30 23:24:46 +01:00
} // switch (m_State)
2016-02-05 23:45:45 +02:00
2014-01-17 10:12:04 +01:00
// Unknown packet type, report to the ClientHandle:
2013-10-30 23:24:46 +01:00
m_Client -> PacketUnknown ( a_PacketType );
2014-01-16 15:26:58 +01:00
return false ;
2013-10-30 23:24:46 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketStatusPing ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
2014-08-11 15:33:20 +02:00
HANDLE_READ ( a_ByteBuffer , ReadBEInt64 , Int64 , Timestamp );
2014-09-04 00:29:36 +02:00
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x01 ); // Ping packet
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt64 ( Timestamp );
2013-10-31 23:48:43 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketStatusRequest ( cByteBuffer & a_ByteBuffer )
2013-10-30 23:24:46 +01:00
{
2014-04-18 21:09:44 +02:00
cServer * Server = cRoot :: Get () -> GetServer ();
2014-08-20 22:22:38 +02:00
AString ServerDescription = Server -> GetDescription ();
2014-08-20 16:01:30 +02:00
int NumPlayers = Server -> GetNumPlayers ();
int MaxPlayers = Server -> GetMaxPlayers ();
AString Favicon = Server -> GetFaviconData ();
2014-08-20 22:22:38 +02:00
cRoot :: Get () -> GetPluginManager () -> CallHookServerPing ( * m_Client , ServerDescription , NumPlayers , MaxPlayers , Favicon );
2014-08-20 16:01:30 +02:00
// Version:
Json :: Value Version ;
2015-09-25 10:14:17 +02:00
Version [ "name" ] = "Cuberite 1.7.2" ;
2014-08-20 16:01:30 +02:00
Version [ "protocol" ] = 4 ;
// Players:
Json :: Value Players ;
Players [ "online" ] = NumPlayers ;
Players [ "max" ] = MaxPlayers ;
// TODO: Add "sample"
// Description:
Json :: Value Description ;
2014-08-20 22:22:38 +02:00
Description [ "text" ] = ServerDescription . c_str ();
2014-08-20 16:01:30 +02:00
// 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 );
2013-11-03 11:58:49 +01:00
cPacketizer Pkt ( * this , 0x00 ); // Response packet
Pkt . WriteString ( Response );
2013-10-30 23:24:46 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketLoginEncryptionResponse ( cByteBuffer & a_ByteBuffer )
2013-10-30 23:38:55 +01:00
{
2015-03-21 13:00:20 +01:00
UInt16 EncKeyLength , EncNonceLength ;
if ( ! a_ByteBuffer . ReadBEUInt16 ( EncKeyLength ))
2015-02-08 14:49:46 +01:00
{
return ;
}
2015-03-21 13:00:20 +01:00
if ( EncKeyLength > MAX_ENC_LEN )
2015-01-18 22:43:35 +01:00
{
2015-03-21 13:00:20 +01:00
LOGD ( "Invalid Encryption Key length: %u (0x%04x). Kicking client." , EncKeyLength , EncKeyLength );
2015-01-18 22:43:35 +01:00
m_Client -> Kick ( "Invalid EncKeyLength" );
return ;
}
2014-01-28 23:53:07 +01:00
AString EncKey ;
2015-03-21 13:00:20 +01:00
if ( ! a_ByteBuffer . ReadString ( EncKey , EncKeyLength ))
2014-01-28 23:53:07 +01:00
{
return ;
}
2015-03-21 13:00:20 +01:00
if ( ! a_ByteBuffer . ReadBEUInt16 ( EncNonceLength ))
2014-12-21 20:01:42 +01:00
{
return ;
}
2015-03-21 13:00:20 +01:00
if ( EncNonceLength > MAX_ENC_LEN )
2014-01-28 23:53:07 +01:00
{
2015-03-21 13:00:20 +01:00
LOGD ( "Invalid Encryption Nonce length: %u (0x%04x). Kicking client." , EncNonceLength , EncNonceLength );
2015-01-18 22:43:35 +01:00
m_Client -> Kick ( "Invalid EncNonceLength" );
2014-01-28 23:53:07 +01:00
return ;
}
2015-01-18 22:43:35 +01:00
AString EncNonce ;
2015-03-21 13:00:20 +01:00
if ( ! a_ByteBuffer . ReadString ( EncNonce , EncNonceLength ))
2014-01-28 23:53:07 +01:00
{
return ;
}
// Decrypt EncNonce using privkey
2014-04-29 11:04:54 +02:00
cRsaPrivateKey & rsaDecryptor = cRoot :: Get () -> GetServer () -> GetPrivateKey ();
2015-03-21 15:40:56 +01:00
UInt32 DecryptedNonce [ MAX_ENC_LEN / sizeof ( UInt32 )];
2015-03-21 13:00:20 +01:00
int res = rsaDecryptor . Decrypt (
reinterpret_cast < const Byte *> ( EncNonce . data ()), EncNonce . size (),
reinterpret_cast < Byte *> ( DecryptedNonce ), sizeof ( DecryptedNonce )
);
2014-01-28 23:53:07 +01:00
if ( res != 4 )
{
LOGD ( "Bad nonce length: got %d, exp %d" , res , 4 );
m_Client -> Kick ( "Hacked client" );
return ;
}
2015-03-21 15:40:56 +01:00
if ( ntohl ( DecryptedNonce [ 0 ]) != static_cast < UInt32 > ( reinterpret_cast < uintptr_t > ( this )))
2014-01-28 23:53:07 +01:00
{
LOGD ( "Bad nonce value" );
m_Client -> Kick ( "Hacked client" );
return ;
}
2016-02-05 23:45:45 +02:00
2014-01-28 23:53:07 +01:00
// Decrypt the symmetric encryption key using privkey:
Byte DecryptedKey [ MAX_ENC_LEN ];
2015-03-21 15:40:56 +01:00
res = rsaDecryptor . Decrypt (
reinterpret_cast < const Byte *> ( EncKey . data ()), EncKey . size (),
DecryptedKey , sizeof ( DecryptedKey )
);
2014-01-28 23:53:07 +01:00
if ( res != 16 )
{
LOGD ( "Bad key length" );
m_Client -> Kick ( "Hacked client" );
return ;
}
2016-02-05 23:45:45 +02:00
2014-01-28 23:53:07 +01:00
StartEncryption ( DecryptedKey );
m_Client -> HandleLogin ( 4 , m_Client -> GetUsername ());
2013-10-31 23:48:43 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketLoginStart ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
AString Username ;
2014-08-21 21:38:57 +02:00
if ( ! a_ByteBuffer . ReadVarUTF8String ( Username ))
{
m_Client -> Kick ( "Bad username" );
return ;
}
2016-02-05 23:45:45 +02:00
2014-01-13 20:32:15 +01:00
if ( ! m_Client -> HandleHandshake ( Username ))
{
// The client is not welcome here, they have been sent a Kick packet already
return ;
}
2016-02-05 23:45:45 +02:00
2014-04-18 21:09:44 +02:00
cServer * Server = cRoot :: Get () -> GetServer ();
2014-01-28 23:53:07 +01:00
// If auth is required, then send the encryption request:
2014-04-18 21:09:44 +02:00
if ( Server -> ShouldAuthenticate ())
2014-01-28 23:53:07 +01:00
{
cPacketizer Pkt ( * this , 0x01 );
2014-04-18 21:09:44 +02:00
Pkt . WriteString ( Server -> GetServerID ());
const AString & PubKeyDer = Server -> GetPublicKeyDER ();
2015-03-22 23:09:23 +01:00
Pkt . WriteBEUInt16 ( static_cast < UInt16 > ( PubKeyDer . size ()));
2014-01-28 23:53:07 +01:00
Pkt . WriteBuf ( PubKeyDer . data (), PubKeyDer . size ());
2015-03-22 23:09:23 +01:00
Pkt . WriteBEInt16 ( 4 );
Pkt . WriteBEUInt32 ( static_cast < UInt32 > ( reinterpret_cast < uintptr_t > ( this ))); // Using 'this' as the cryptographic nonce, so that we don't have to generate one each time :)
2014-01-28 23:53:07 +01:00
m_Client -> SetUsername ( Username );
return ;
}
2016-02-05 23:45:45 +02:00
2013-10-31 23:48:43 +01:00
m_Client -> HandleLogin ( 4 , Username );
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketAnimation ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
2015-03-21 13:00:20 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEUInt32 , UInt32 , EntityID );
HANDLE_READ ( a_ByteBuffer , ReadBEUInt8 , UInt8 , Animation );
2013-10-31 23:48:43 +01:00
m_Client -> HandleAnimation ( Animation );
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketBlockDig ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
2015-03-21 13:00:20 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEUInt8 , UInt8 , Status );
HANDLE_READ ( a_ByteBuffer , ReadBEInt32 , Int32 , BlockX );
HANDLE_READ ( a_ByteBuffer , ReadBEUInt8 , UInt8 , BlockY );
HANDLE_READ ( a_ByteBuffer , ReadBEInt32 , Int32 , BlockZ );
HANDLE_READ ( a_ByteBuffer , ReadBEInt8 , Int8 , Face );
m_Client -> HandleLeftClick ( BlockX , BlockY , BlockZ , FaceIntToBlockFace ( Face ), Status );
2013-10-31 23:48:43 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketBlockPlace ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
2015-03-21 13:00:20 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEInt32 , Int32 , BlockX );
HANDLE_READ ( a_ByteBuffer , ReadBEUInt8 , UInt8 , BlockY );
HANDLE_READ ( a_ByteBuffer , ReadBEInt32 , Int32 , BlockZ );
2015-03-21 15:40:56 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEInt8 , Int8 , Face );
2013-12-02 09:32:28 -07:00
cItem Item ;
2013-12-13 17:53:00 +01:00
ReadItem ( a_ByteBuffer , Item );
2013-12-01 20:40:12 -07:00
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 ());
2013-10-31 23:48:43 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketChatMessage ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
2013-12-13 17:53:00 +01:00
HANDLE_READ ( a_ByteBuffer , ReadVarUTF8String , AString , Message );
2013-10-31 23:48:43 +01:00
m_Client -> HandleChat ( Message );
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketClientSettings ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
2013-12-13 17:53:00 +01:00
HANDLE_READ ( a_ByteBuffer , ReadVarUTF8String , AString , Locale );
2015-03-21 13:00:20 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEUInt8 , UInt8 , ViewDistance );
HANDLE_READ ( a_ByteBuffer , ReadBEUInt8 , UInt8 , ChatFlags );
HANDLE_READ ( a_ByteBuffer , ReadBEUInt8 , UInt8 , ChatColors );
HANDLE_READ ( a_ByteBuffer , ReadBEUInt8 , UInt8 , Difficulty );
HANDLE_READ ( a_ByteBuffer , ReadBool , bool , ShowCape );
2016-02-05 23:45:45 +02:00
2014-02-16 13:26:07 +01:00
m_Client -> SetLocale ( Locale );
2014-10-02 23:50:41 +02:00
m_Client -> SetViewDistance ( ViewDistance );
2014-09-04 19:03:21 +02:00
// TODO: Do anything with the other values.
2013-10-31 23:48:43 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketClientStatus ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
2015-03-21 13:00:20 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEUInt8 , UInt8 , ActionID );
2013-11-03 21:34:36 +01:00
switch ( ActionID )
{
case 0 :
{
// Respawn
m_Client -> HandleRespawn ();
break ;
}
case 1 :
{
// Request stats
2014-05-11 14:57:06 +03:00
const cStatManager & Manager = m_Client -> GetPlayer () -> GetStatManager ();
SendStatistics ( Manager );
2013-11-03 21:34:36 +01:00
break ;
}
case 2 :
{
// Open Inventory achievement
2014-05-12 17:05:09 +03:00
m_Client -> GetPlayer () -> AwardAchievement ( achOpenInv );
2013-11-03 21:34:36 +01:00
break ;
}
}
2013-10-31 23:48:43 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketCreativeInventoryAction ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
2015-03-21 13:00:20 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEInt16 , Int16 , SlotNum );
2013-11-06 20:48:35 +01:00
cItem Item ;
2013-12-13 17:53:00 +01:00
if ( ! ReadItem ( a_ByteBuffer , Item ))
2013-11-06 20:48:35 +01:00
{
return ;
}
2015-03-21 13:00:20 +01:00
m_Client -> HandleCreativeInventory ( SlotNum , Item , ( SlotNum < 0 ) ? caLeftClick : caLeftClickOutside );
2013-10-31 23:48:43 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketEntityAction ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
2015-03-21 13:00:20 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEUInt32 , UInt32 , PlayerID );
HANDLE_READ ( a_ByteBuffer , ReadBEUInt8 , UInt8 , Action );
HANDLE_READ ( a_ByteBuffer , ReadBEInt32 , Int32 , JumpBoost );
2014-03-08 17:55:53 +01:00
2014-03-08 19:26:32 +01:00
switch ( Action )
2014-03-08 17:55:53 +01:00
{
2014-07-17 22:15:34 +02:00
case 1 : m_Client -> HandleEntityCrouch ( PlayerID , true ); break ; // Crouch
case 2 : m_Client -> HandleEntityCrouch ( PlayerID , false ); break ; // Uncrouch
case 3 : m_Client -> HandleEntityLeaveBed ( PlayerID ); break ; // Leave Bed
case 4 : m_Client -> HandleEntitySprinting ( PlayerID , true ); break ; // Start sprinting
case 5 : m_Client -> HandleEntitySprinting ( PlayerID , false ); break ; // Stop sprinting
2014-03-08 17:55:53 +01:00
}
2013-10-31 23:48:43 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketKeepAlive ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
2015-08-05 01:24:59 +03:00
HANDLE_READ ( a_ByteBuffer , ReadBEUInt32 , UInt32 , KeepAliveID );
2013-11-06 22:27:09 +01:00
m_Client -> HandleKeepAlive ( KeepAliveID );
2013-10-31 23:48:43 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketPlayer ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
2013-12-13 17:53:00 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBool , bool , IsOnGround );
2013-11-06 22:27:09 +01:00
// TODO: m_Client->HandlePlayerOnGround(IsOnGround);
2013-10-31 23:48:43 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketPlayerAbilities ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
2015-03-21 13:00:20 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEUInt8 , UInt8 , Flags );
2013-12-13 17:53:00 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEFloat , float , FlyingSpeed );
HANDLE_READ ( a_ByteBuffer , ReadBEFloat , float , WalkingSpeed );
2013-12-15 15:11:59 +01:00
2015-03-21 13:00:20 +01:00
// Convert flags bitfield into individual bool flags:
2013-12-19 20:53:47 +00:00
bool IsFlying = false , CanFly = false ;
2013-12-15 15:11:59 +01:00
if (( Flags & 2 ) != 0 )
{
IsFlying = true ;
}
if (( Flags & 4 ) != 0 )
{
CanFly = true ;
}
2013-12-19 20:53:47 +00:00
2013-12-15 15:11:59 +01:00
m_Client -> HandlePlayerAbilities ( CanFly , IsFlying , FlyingSpeed , WalkingSpeed );
2013-10-31 23:48:43 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketPlayerLook ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
2013-12-13 17:53:00 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEFloat , float , Yaw );
HANDLE_READ ( a_ByteBuffer , ReadBEFloat , float , Pitch );
HANDLE_READ ( a_ByteBuffer , ReadBool , bool , IsOnGround );
2013-11-06 22:27:09 +01:00
m_Client -> HandlePlayerLook ( Yaw , Pitch , IsOnGround );
2013-10-31 23:48:43 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketPlayerPos ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
2013-12-13 17:53:00 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEDouble , double , PosX );
HANDLE_READ ( a_ByteBuffer , ReadBEDouble , double , PosY );
HANDLE_READ ( a_ByteBuffer , ReadBEDouble , double , Stance );
HANDLE_READ ( a_ByteBuffer , ReadBEDouble , double , PosZ );
HANDLE_READ ( a_ByteBuffer , ReadBool , bool , IsOnGround );
2013-11-06 22:27:09 +01:00
m_Client -> HandlePlayerPos ( PosX , PosY , PosZ , Stance , IsOnGround );
2013-10-31 23:48:43 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketPlayerPosLook ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
2013-12-13 17:53:00 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEDouble , double , PosX );
HANDLE_READ ( a_ByteBuffer , ReadBEDouble , double , PosY );
HANDLE_READ ( a_ByteBuffer , ReadBEDouble , double , Stance );
HANDLE_READ ( a_ByteBuffer , ReadBEDouble , double , PosZ );
HANDLE_READ ( a_ByteBuffer , ReadBEFloat , float , Yaw );
HANDLE_READ ( a_ByteBuffer , ReadBEFloat , float , Pitch );
HANDLE_READ ( a_ByteBuffer , ReadBool , bool , IsOnGround );
2013-11-06 22:27:09 +01:00
m_Client -> HandlePlayerMoveLook ( PosX , PosY , PosZ , Stance , Yaw , Pitch , IsOnGround );
2013-10-31 23:48:43 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketPluginMessage ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
2013-12-13 17:53:00 +01:00
HANDLE_READ ( a_ByteBuffer , ReadVarUTF8String , AString , Channel );
2015-03-21 13:00:20 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEUInt16 , UInt16 , Length );
if ( Length != a_ByteBuffer . GetReadableSpace () - 1 )
2014-09-30 13:33:57 +02:00
{
2015-03-21 13:00:20 +01:00
LOGD ( "Invalid plugin message packet, payload length doesn't match packet length (exp %u, got %u)" ,
2015-03-21 15:40:56 +01:00
static_cast < unsigned > ( a_ByteBuffer . GetReadableSpace () - 1 ), Length
2014-09-30 13:33:57 +02:00
);
return ;
}
2016-02-05 23:45:45 +02:00
2014-09-30 13:33:57 +02:00
// If the plugin channel is recognized vanilla, handle it directly:
if ( Channel . substr ( 0 , 3 ) == "MC|" )
{
HandleVanillaPluginMessage ( a_ByteBuffer , Channel , Length );
return ;
}
2016-02-05 23:45:45 +02:00
2014-09-30 13:33:57 +02:00
// Read the plugin message and relay to clienthandle:
2013-11-06 22:27:09 +01:00
AString Data ;
2015-03-21 13:00:20 +01:00
if ( ! a_ByteBuffer . ReadString ( Data , Length ))
2014-08-11 15:33:20 +02:00
{
return ;
}
2014-01-07 17:47:05 +01:00
m_Client -> HandlePluginMessage ( Channel , Data );
2013-10-31 23:48:43 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketSlotSelect ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
2015-03-21 16:11:57 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEInt16 , Int16 , SlotNum );
2013-11-06 22:27:09 +01:00
m_Client -> HandleSlotSelected ( SlotNum );
2013-10-31 23:48:43 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketSteerVehicle ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
2013-12-13 17:53:00 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEFloat , float , Forward );
HANDLE_READ ( a_ByteBuffer , ReadBEFloat , float , Sideways );
HANDLE_READ ( a_ByteBuffer , ReadBool , bool , ShouldJump );
HANDLE_READ ( a_ByteBuffer , ReadBool , bool , ShouldUnmount );
2013-11-06 22:27:09 +01:00
if ( ShouldUnmount )
{
m_Client -> HandleUnmount ();
}
else
{
m_Client -> HandleSteerVehicle ( Forward , Sideways );
}
2013-10-31 23:48:43 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketTabComplete ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
2013-12-13 17:53:00 +01:00
HANDLE_READ ( a_ByteBuffer , ReadVarUTF8String , AString , Text );
2013-11-06 22:27:09 +01:00
m_Client -> HandleTabCompletion ( Text );
2013-10-31 23:48:43 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketUpdateSign ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
2015-03-21 13:00:20 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEInt32 , Int32 , BlockX );
HANDLE_READ ( a_ByteBuffer , ReadBEInt16 , Int16 , BlockY );
HANDLE_READ ( a_ByteBuffer , ReadBEInt32 , Int32 , BlockZ );
2013-12-13 17:53:00 +01:00
HANDLE_READ ( a_ByteBuffer , ReadVarUTF8String , AString , Line1 );
HANDLE_READ ( a_ByteBuffer , ReadVarUTF8String , AString , Line2 );
HANDLE_READ ( a_ByteBuffer , ReadVarUTF8String , AString , Line3 );
HANDLE_READ ( a_ByteBuffer , ReadVarUTF8String , AString , Line4 );
2013-11-06 22:27:09 +01:00
m_Client -> HandleUpdateSign ( BlockX , BlockY , BlockZ , Line1 , Line2 , Line3 , Line4 );
2013-10-31 23:48:43 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketUseEntity ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
2015-03-21 13:00:20 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEUInt32 , UInt32 , EntityID );
HANDLE_READ ( a_ByteBuffer , ReadBEUInt8 , UInt8 , MouseButton );
2013-11-08 21:03:51 +01:00
m_Client -> HandleUseEntity ( EntityID , ( MouseButton == 1 ));
2013-10-31 23:48:43 +01:00
}
2014-01-31 19:46:51 +01:00
void cProtocol172 :: HandlePacketEnchantItem ( cByteBuffer & a_ByteBuffer )
2014-01-20 18:22:08 +01:00
{
2015-03-21 13:00:20 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEUInt8 , UInt8 , WindowID );
HANDLE_READ ( a_ByteBuffer , ReadBEUInt8 , UInt8 , Enchantment );
2014-01-20 18:22:08 +01:00
2014-01-31 19:46:51 +01:00
m_Client -> HandleEnchantItem ( WindowID , Enchantment );
2014-01-20 18:22:08 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketWindowClick ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
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 );
2013-11-06 22:27:09 +01:00
cItem Item ;
2013-12-13 17:53:00 +01:00
ReadItem ( a_ByteBuffer , Item );
2013-11-06 22:27:09 +01:00
// Convert Button, Mode, SlotNum and HeldItem into eClickAction:
eClickAction Action ;
switch (( Mode << 8 ) | Button )
{
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 ;
2013-11-06 22:27:09 +01: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 ;
2013-11-06 22:27:09 +01:00
case 0x0600 : Action = caDblClick ; break ;
2013-12-13 17:53:00 +01:00
default :
{
LOGWARNING ( "Unhandled window click mode / button combination: %d (0x%x)" , ( Mode << 8 ) | Button , ( Mode << 8 ) | Button );
Action = caUnknown ;
break ;
}
2013-11-06 22:27:09 +01:00
}
m_Client -> HandleWindowClick ( WindowID , SlotNum , Action , Item );
2013-10-31 23:48:43 +01:00
}
2013-12-13 17:53:00 +01:00
void cProtocol172 :: HandlePacketWindowClose ( cByteBuffer & a_ByteBuffer )
2013-10-31 23:48:43 +01:00
{
2015-03-21 13:00:20 +01:00
HANDLE_READ ( a_ByteBuffer , ReadBEUInt8 , UInt8 , WindowID );
2013-11-06 22:27:09 +01:00
m_Client -> HandleWindowClose ( WindowID );
2013-10-30 23:38:55 +01:00
}
2015-03-21 13:00:20 +01:00
void cProtocol172 :: HandleVanillaPluginMessage ( cByteBuffer & a_ByteBuffer , const AString & a_Channel , UInt16 a_PayloadLength )
2013-10-30 23:24:46 +01:00
{
2014-09-30 13:33:57 +02:00
if ( a_Channel == "MC|AdvCdm" )
{
2015-01-03 22:23:49 +01:00
size_t BeginningSpace = a_ByteBuffer . GetReadableSpace ();
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 :
{
// Block-based commandblock update:
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 ;
}
// TODO: Entity-based commandblock update
2016-02-05 23:45:45 +02:00
2014-09-30 13:33:57 +02:00
default :
{
m_Client -> SendChat ( Printf ( "Failure setting command block command; unhandled mode %d" , Mode ), mtFailure );
LOG ( "Unhandled MC|AdvCdm packet mode." );
return ;
}
} // switch (Mode)
2015-01-03 22:23:49 +01:00
// Read the remainder of the packet (Vanilla sometimes sends bogus data at the end of the packet; #1692):
size_t BytesRead = BeginningSpace - a_ByteBuffer . GetReadableSpace ();
2015-03-21 13:00:20 +01:00
if ( BytesRead < a_PayloadLength )
2015-01-03 22:23:49 +01:00
{
LOGD ( "Protocol 1.7: Skipping garbage data at the end of a vanilla MC|AdvCdm packet, %u bytes" ,
2015-01-03 22:39:55 +01:00
static_cast < unsigned > ( a_PayloadLength - BytesRead )
2015-01-03 22:23:49 +01:00
);
2015-03-21 13:00:20 +01:00
a_ByteBuffer . SkipRead ( a_PayloadLength - BytesRead );
2015-01-03 22:23:49 +01:00
}
2014-09-30 13:33:57 +02:00
return ;
}
else if ( a_Channel == "MC|Brand" )
{
// Read the client's brand:
AString Brand ;
2015-03-21 13:00:20 +01:00
if ( a_ByteBuffer . ReadString ( Brand , a_PayloadLength ))
2014-09-30 13:33:57 +02:00
{
m_Client -> SetClientBrand ( Brand );
}
2016-02-05 23:45:45 +02:00
2014-09-30 13:33:57 +02:00
// Send back our brand:
2015-09-25 10:14:17 +02:00
SendPluginMessage ( "MC|Brand" , "Cuberite" );
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" )
{
AString ItemName ;
2015-03-21 13:00:20 +01:00
if ( a_ByteBuffer . ReadString ( ItemName , a_PayloadLength ))
2014-09-30 13:33:57 +02:00
{
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 ());
2016-02-05 23:45:45 +02:00
2014-09-30 13:33:57 +02:00
// Read the payload and send it through to the clienthandle:
AString Message ;
2015-03-21 13:00:20 +01:00
VERIFY ( a_ByteBuffer . ReadString ( Message , a_PayloadLength ));
2014-09-30 13:33:57 +02:00
m_Client -> HandlePluginMessage ( a_Channel , Message );
2013-10-30 23:24:46 +01:00
}
2014-04-04 10:13:25 +02:00
void cProtocol172 :: SendData ( const char * a_Data , size_t a_Size )
2013-10-30 23:24:46 +01:00
{
if ( m_IsEncrypted )
{
2014-01-23 23:35:23 +01:00
Byte Encrypted [ 8192 ]; // Larger buffer, we may be sending lots of data (chunks)
2013-10-30 23:24:46 +01:00
while ( a_Size > 0 )
{
2014-04-04 10:13:25 +02:00
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 < const Byte *> ( a_Data ), NumBytes );
m_Client -> SendData ( reinterpret_cast < const char *> ( Encrypted ), NumBytes );
2013-10-30 23:24:46 +01:00
a_Size -= NumBytes ;
a_Data += NumBytes ;
}
}
else
{
m_Client -> SendData ( a_Data , a_Size );
}
}
2015-03-22 19:46:08 +01:00
void cProtocol172 :: SendPacket ( cPacketizer & a_Packet )
{
AString DataToSend ;
// Send the packet length
UInt32 PacketLen = static_cast < UInt32 > ( m_OutPacketBuffer . GetUsedSpace ());
2015-03-22 23:09:23 +01:00
m_OutPacketLenBuffer . WriteVarInt32 ( PacketLen );
2015-03-22 19:46:08 +01:00
m_OutPacketLenBuffer . ReadAll ( DataToSend );
SendData ( DataToSend . data (), DataToSend . size ());
m_OutPacketLenBuffer . CommitRead ();
2016-02-05 23:45:45 +02:00
2015-03-22 19:46:08 +01:00
// Send the packet data:
m_OutPacketBuffer . ReadAll ( DataToSend );
SendData ( DataToSend . data (), DataToSend . size ());
m_OutPacketBuffer . CommitRead ();
2016-02-05 23:45:45 +02:00
2015-03-22 19:46:08 +01:00
// Log the comm into logfile:
if ( g_ShouldLogCommOut )
{
AString Hex ;
ASSERT ( DataToSend . size () > 0 );
CreateHexDump ( Hex , DataToSend . data (), DataToSend . size (), 16 );
m_CommLogFile . Printf ( "Outgoing packet: type %d (0x%x), length %u (0x%x), state %d. Payload (incl. type): \n %s \n " ,
a_Packet . GetPacketType (), a_Packet . GetPacketType (), PacketLen , PacketLen , m_State , Hex . c_str ()
);
}
}
2013-12-13 17:53:00 +01:00
bool cProtocol172 :: ReadItem ( cByteBuffer & a_ByteBuffer , cItem & a_Item )
2013-11-06 20:48:35 +01:00
{
2015-03-21 13:00:20 +01:00
HANDLE_PACKET_READ ( a_ByteBuffer , ReadBEInt16 , Int16 , ItemType );
2013-11-06 20:48:35 +01:00
if ( ItemType == - 1 )
{
// The item is empty, no more data follows
a_Item . Empty ();
return true ;
}
a_Item . m_ItemType = ItemType ;
2016-02-05 23:45:45 +02:00
2015-03-21 13:00:20 +01:00
HANDLE_PACKET_READ ( a_ByteBuffer , ReadBEInt8 , Int8 , ItemCount );
HANDLE_PACKET_READ ( a_ByteBuffer , ReadBEInt16 , Int16 , ItemDamage );
2013-11-06 20:48:35 +01:00
a_Item . m_ItemCount = ItemCount ;
a_Item . m_ItemDamage = ItemDamage ;
if ( ItemCount <= 0 )
{
a_Item . Empty ();
}
// Read the metadata
2015-03-21 13:00:20 +01:00
HANDLE_PACKET_READ ( a_ByteBuffer , ReadBEUInt16 , UInt16 , MetadataLength );
2013-11-06 20:48:35 +01:00
AString Metadata ;
2015-03-21 13:00:20 +01:00
if ( ! a_ByteBuffer . ReadString ( Metadata , MetadataLength ))
2013-11-06 20:48:35 +01:00
{
return false ;
}
2016-02-05 23:45:45 +02:00
2013-11-06 20:48:35 +01:00
ParseItemMetadata ( a_Item , Metadata );
return true ;
}
void cProtocol172 :: ParseItemMetadata ( cItem & a_Item , const AString & a_Metadata )
{
// Uncompress the GZIPped data:
AString Uncompressed ;
if ( UncompressStringGZIP ( a_Metadata . data (), a_Metadata . size (), Uncompressed ) != Z_OK )
{
AString HexDump ;
CreateHexDump ( HexDump , a_Metadata . data (), a_Metadata . size (), 16 );
2014-03-12 10:34:50 -07:00
LOGWARNING ( "Cannot unGZIP item metadata (" SIZE_T_FMT " bytes): \n %s" , a_Metadata . size (), HexDump . c_str ());
2013-11-06 20:48:35 +01:00
return ;
}
2016-02-05 23:45:45 +02:00
2013-11-06 20:48:35 +01:00
// Parse into NBT:
cParsedNBT NBT ( Uncompressed . data (), Uncompressed . size ());
if ( ! NBT . IsValid ())
{
AString HexDump ;
CreateHexDump ( HexDump , Uncompressed . data (), Uncompressed . size (), 16 );
2014-03-12 10:34:50 -07:00
LOGWARNING ( "Cannot parse NBT item metadata: (" SIZE_T_FMT " bytes) \n %s" , Uncompressed . size (), HexDump . c_str ());
2013-11-06 20:48:35 +01:00
return ;
}
2016-02-05 23:45:45 +02:00
2014-01-15 22:38:03 +00:00
// Load enchantments and custom display names from the NBT data:
2013-11-06 20:48:35 +01:00
for ( int tag = NBT . GetFirstChild ( NBT . GetRoot ()); tag >= 0 ; tag = NBT . GetNextSibling ( tag ))
{
2014-02-26 23:29:14 +00:00
AString TagName = NBT . GetName ( tag );
switch ( NBT . GetType ( tag ))
2014-01-15 22:38:03 +00:00
{
2014-02-26 23:29:14 +00:00
case TAG_List :
2014-01-15 22:38:03 +00:00
{
2014-07-17 22:15:34 +02:00
if (( TagName == "ench" ) || ( TagName == "StoredEnchantments" )) // Enchantments tags
2014-01-15 22:38:03 +00:00
{
2014-02-26 23:29:14 +00:00
EnchantmentSerializer :: ParseFromNBT ( a_Item . m_Enchantments , NBT , tag );
2014-01-15 22:38:03 +00:00
}
2014-02-26 23:29:14 +00:00
break ;
}
case TAG_Compound :
{
2014-07-17 22:15:34 +02:00
if ( TagName == "display" ) // Custom name and lore tag
2014-01-15 22:38:03 +00:00
{
2014-02-26 23:29:14 +00:00
for ( int displaytag = NBT . GetFirstChild ( tag ); displaytag >= 0 ; displaytag = NBT . GetNextSibling ( displaytag ))
2014-01-15 22:38:03 +00:00
{
2014-07-17 22:15:34 +02:00
if (( NBT . GetType ( displaytag ) == TAG_String ) && ( NBT . GetName ( displaytag ) == "Name" )) // Custon name tag
2014-02-26 23:29:14 +00:00
{
a_Item . m_CustomName = NBT . GetString ( displaytag );
}
2014-07-17 22:15:34 +02:00
else if (( NBT . GetType ( displaytag ) == TAG_List ) && ( NBT . GetName ( displaytag ) == "Lore" )) // Lore tag
2014-02-26 23:29:14 +00:00
{
AString Lore ;
2014-07-17 22:15:34 +02:00
for ( int loretag = NBT . GetFirstChild ( displaytag ); loretag >= 0 ; loretag = NBT . GetNextSibling ( loretag )) // Loop through array of strings
2014-02-26 23:29:14 +00:00
{
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-02-26 23:29:14 +00: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-01-15 22:38:03 +00:00
}
}
2014-02-26 23:29:14 +00: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-02-26 23:29:14 +00:00
}
break ;
2014-01-15 22:38:03 +00:00
}
2014-05-07 12:30:30 +02:00
case TAG_Int :
{
if ( TagName == "RepairCost" )
{
2014-05-07 20:43:37 +02:00
a_Item . m_RepairCost = NBT . GetInt ( tag );
2014-05-07 12:30:30 +02:00
}
}
2014-02-26 23:29:14 +00:00
default : LOGD ( "Unimplemented NBT data when parsing!" ); break ;
2014-01-15 22:38:03 +00:00
}
2013-11-06 20:48:35 +01:00
}
}
2014-01-28 23:53:07 +01:00
void cProtocol172 :: StartEncryption ( const Byte * a_Key )
{
m_Encryptor . Init ( a_Key , a_Key );
m_Decryptor . Init ( a_Key , a_Key );
m_IsEncrypted = true ;
2016-02-05 23:45:45 +02:00
2014-01-28 23:53:07 +01:00
// Prepare the m_AuthServerID:
2014-04-29 17:37:15 +02:00
cSha1Checksum Checksum ;
2014-04-18 21:09:44 +02:00
cServer * Server = cRoot :: Get () -> GetServer ();
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-01-28 23:53:07 +01: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-01-28 23:53:07 +01:00
Byte Digest [ 20 ];
Checksum . Finalize ( Digest );
2014-04-29 17:37:15 +02:00
cSha1Checksum :: DigestToJava ( Digest , m_AuthServerID );
2014-01-28 23:53:07 +01:00
}
2015-03-21 13:00:20 +01:00
eBlockFace cProtocol172 :: 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 ;
}
}
2015-03-22 19:46:08 +01:00
void cProtocol172 :: WriteItem ( cPacketizer & a_Pkt , const cItem & a_Item )
2013-11-03 11:58:49 +01: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 ;
}
2016-02-05 23:45:45 +02:00
2013-11-03 11:58:49 +01:00
if ( a_Item . IsEmpty ())
{
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEInt16 ( - 1 );
2013-11-03 11:58:49 +01:00
return ;
}
2016-02-05 23:45:45 +02:00
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 );
2016-02-05 23:45:45 +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 ())
2013-11-03 11:58:49 +01:00
{
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEInt16 ( - 1 );
2013-11-03 11:58:49 +01:00
return ;
}
2014-01-15 22:38:03 +00:00
// Send the enchantments and custom names:
2013-11-03 11:58:49 +01:00
cFastNBTWriter Writer ;
2014-05-07 12:30:30 +02:00
if ( a_Item . m_RepairCost != 0 )
{
2014-05-07 20:43:37 +02:00
Writer . AddInt ( "RepairCost" , a_Item . m_RepairCost );
2014-05-07 12:30:30 +02:00
}
2014-01-15 22:38:03 +00:00
if ( ! a_Item . m_Enchantments . IsEmpty ())
{
const char * TagName = ( a_Item . m_ItemType == E_ITEM_BOOK ) ? "StoredEnchantments" : "ench" ;
2014-07-19 14:53:41 +02:00
EnchantmentSerializer :: WriteToNBTCompound ( a_Item . m_Enchantments , Writer , TagName );
2014-01-15 22:38:03 +00:00
}
2015-07-13 18:15:37 -06:00
if ( ! a_Item . IsBothNameAndLoreEmpty () || a_Item . m_ItemColor . IsValid ())
2014-01-15 22:38:03 +00:00
{
Writer . BeginCompound ( "display" );
2015-07-13 18:15:37 -06:00
if ( a_Item . m_ItemColor . IsValid ())
{
Writer . AddInt ( "color" , static_cast < int > ( a_Item . m_ItemColor . m_Color ));
}
2014-01-15 22:38:03 +00:00
if ( ! a_Item . IsCustomNameEmpty ())
{
Writer . AddString ( "Name" , a_Item . m_CustomName . c_str ());
}
if ( ! a_Item . IsLoreEmpty ())
{
Writer . BeginList ( "Lore" , TAG_String );
2014-01-16 22:30:57 +00:00
AStringVector Decls = StringSplit ( a_Item . m_Lore , "`" );
2014-01-15 22:38:03 +00:00
for ( AStringVector :: const_iterator itr = Decls . begin (), end = Decls . end (); itr != end ; ++ itr )
{
if ( itr -> empty ())
{
2014-01-16 22:30:57 +00:00
// The decl is empty (two `s), ignore
2014-01-15 22:38:03 +00:00
continue ;
}
Writer . AddString ( "" , itr -> c_str ());
}
Writer . EndList ();
}
Writer . EndCompound ();
}
2014-02-26 23:29:14 +00:00
if (( a_Item . m_ItemType == E_ITEM_FIREWORK_ROCKET ) || ( a_Item . m_ItemType == E_ITEM_FIREWORK_STAR ))
{
2015-07-29 09:04:03 -06:00
cFireworkItem :: WriteToNBTCompound ( a_Item . m_FireworkItem , Writer , static_cast < ENUM_ITEM_ID > ( a_Item . m_ItemType ));
2014-02-26 23:29:14 +00:00
}
2013-11-03 11:58:49 +01:00
Writer . Finish ();
2014-09-08 17:02:54 +02:00
2013-11-03 11:58:49 +01:00
AString Compressed ;
CompressStringGZIP ( Writer . GetResult (). data (), Writer . GetResult (). size (), Compressed );
2015-03-22 19:46:08 +01:00
a_Pkt . WriteBEUInt16 ( static_cast < UInt16 > ( Compressed . size ()));
a_Pkt . WriteBuf ( Compressed . data (), Compressed . size ());
2013-11-03 11:58:49 +01:00
}
2014-09-04 03:22:35 +02:00
2015-03-22 19:46:08 +01:00
void cProtocol172 :: WriteBlockEntity ( cPacketizer & a_Pkt , const cBlockEntity & a_BlockEntity )
2014-01-19 00:54:38 +00:00
{
2014-01-19 13:25:35 +00:00
cFastNBTWriter Writer ;
switch ( a_BlockEntity . GetBlockType ())
{
2014-07-30 21:59:35 +02:00
case E_BLOCK_BEACON :
{
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-07-31 12:13:11 +02:00
Writer . AddInt ( "Secondary" , BeaconEntity . GetSecondaryEffect ());
2015-03-22 19:46:08 +01:00
Writer . AddInt ( "Levels" , BeaconEntity . GetBeaconLevel ());
2014-07-30 21:59:35 +02:00
Writer . AddString ( "id" , "Beacon" ); // "Tile Entity ID" - MC wiki; vanilla server always seems to send this though
break ;
}
2015-03-22 19:46:08 +01:00
2014-01-19 13:25:35 +00:00
case E_BLOCK_COMMAND_BLOCK :
{
2015-03-22 19:46:08 +01:00
auto & CommandBlockEntity = reinterpret_cast < const cCommandBlockEntity &> ( a_BlockEntity );
2014-07-17 22:15:34 +02:00
Writer . AddByte ( "TrackOutput" , 1 ); // Neither I nor the MC wiki has any idea about this
2014-01-19 13:25:35 +00:00
Writer . AddInt ( "SuccessCount" , CommandBlockEntity . GetResult ());
2015-03-22 19:46:08 +01:00
Writer . AddInt ( "x" , CommandBlockEntity . GetPosX ());
Writer . AddInt ( "y" , CommandBlockEntity . GetPosY ());
Writer . AddInt ( "z" , CommandBlockEntity . GetPosZ ());
Writer . AddString ( "Command" , CommandBlockEntity . GetCommand (). c_str ());
2014-01-19 13:25:35 +00:00
// 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
2015-03-22 19:46:08 +01:00
// MCS doesn't have this, so just leave it at '@'.
2014-01-19 13:25:35 +00:00
Writer . AddString ( "CustomName" , "@" );
2015-03-22 19:46:08 +01:00
Writer . AddString ( "id" , "Control" ); // "Tile Entity ID" - MC wiki; vanilla server always seems to send this though
2014-01-19 13:25:35 +00:00
if ( ! CommandBlockEntity . GetLastOutput (). empty ())
{
2015-03-22 19:46:08 +01:00
Writer . AddString ( "LastOutput" , Printf ( "{ \" text \" : \" %s \" }" , CommandBlockEntity . GetLastOutput (). c_str ()));
2014-01-19 13:25:35 +00:00
}
break ;
}
2015-03-22 19:46:08 +01:00
2014-02-17 20:14:08 +01:00
case E_BLOCK_HEAD :
{
2015-03-22 19:46:08 +01:00
auto & MobHeadEntity = reinterpret_cast < const cMobHeadEntity &> ( a_BlockEntity );
2014-02-19 14:45:09 +01: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 );
2016-01-11 17:55:32 +01:00
Writer . AddString ( "ExtraType" , MobHeadEntity . GetOwnerName ());
2014-07-17 22:15:34 +02:00
Writer . AddString ( "id" , "Skull" ); // "Tile Entity ID" - MC wiki; vanilla server always seems to send this though
2014-02-19 14:12:34 +01:00
break ;
2014-02-17 20:14:08 +01:00
}
2015-03-22 19:46:08 +01:00
2014-03-07 01:30:34 +01:00
case E_BLOCK_FLOWER_POT :
{
2015-03-22 19:46:08 +01:00
auto & FlowerPotEntity = reinterpret_cast < const cFlowerPotEntity &> ( a_BlockEntity );
2014-03-07 01:30:34 +01: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-07-17 22:15:34 +02:00
Writer . AddString ( "id" , "FlowerPot" ); // "Tile Entity ID" - MC wiki; vanilla server always seems to send this though
2014-03-07 01:30:34 +01:00
break ;
}
2015-03-22 19:46:08 +01:00
2014-09-19 23:00:54 +02:00
case E_BLOCK_MOB_SPAWNER :
{
2015-03-22 19:46:08 +01:00
auto & MobSpawnerEntity = reinterpret_cast < const cMobSpawnerEntity &> ( a_BlockEntity );
2014-09-19 23:00:54 +02: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-09-19 23:00:54 +02:00
Writer . AddShort ( "Delay" , MobSpawnerEntity . GetSpawnDelay ());
Writer . AddString ( "id" , "MobSpawner" );
break ;
}
2015-03-22 19:46:08 +01:00
default :
{
break ;
}
2014-01-19 13:25:35 +00:00
}
Writer . Finish ();
2014-01-19 00:54:38 +00:00
AString Compressed ;
2014-01-19 13:25:35 +00:00
CompressStringGZIP ( Writer . GetResult (). data (), Writer . GetResult (). size (), Compressed );
2015-03-22 19:46:08 +01:00
a_Pkt . WriteBEUInt16 ( static_cast < UInt16 > ( Compressed . size ()));
a_Pkt . WriteBuf ( Compressed . data (), Compressed . size ());
2013-11-03 11:58:49 +01:00
}
2015-03-22 19:46:08 +01:00
void cProtocol172 :: WriteEntityMetadata ( cPacketizer & a_Pkt , const cEntity & a_Entity )
2013-11-04 21:20:36 +01: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 );
2016-02-05 23:45:45 +02:00
2013-11-04 21:20:36 +01:00
switch ( a_Entity . GetEntityType ())
{
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 ());
2013-11-04 21:20:36 +01:00
break ;
}
2013-11-10 20:48:12 +00:00
case cEntity :: etMinecart :
{
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0x51 );
2013-11-10 20:48:12 +00: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:
2014-07-17 23:15:53 +02:00
/*
Health: 5 | 3 - (5 - 3) = 1 (shake power)
Health: 3 | 3 - (3 - 3) = 3
Health: 1 | 3 - (1 - 3) = 5
2013-11-10 20:48:12 +00:00
*/
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
2016-02-05 23:45:45 +02:00
2015-03-22 19:46:08 +01:00
if ( Minecart . GetPayload () == cMinecart :: mpNone )
2014-01-12 15:27:50 +01:00
{
2015-03-22 19:46:08 +01:00
auto & RideableMinecart = reinterpret_cast < const cRideableMinecart &> ( Minecart );
2014-04-18 21:09:44 +02:00
const cItem & MinecartContent = RideableMinecart . GetContent ();
if ( ! MinecartContent . IsEmpty ())
2014-01-12 15:27:50 +01:00
{
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0x54 );
2014-04-18 21:09:44 +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-01-12 15:27:50 +01:00
}
}
2015-03-22 19:46:08 +01:00
else if ( Minecart . GetPayload () == cMinecart :: mpFurnace )
2013-11-10 20:48:12 +00:00
{
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0x10 );
a_Pkt . WriteBEUInt8 ( reinterpret_cast < const cMinecartWithFurnace &> ( Minecart ). IsFueled () ? 1 : 0 );
2013-11-10 20:48:12 +00:00
}
break ;
2015-03-22 19:46:08 +01:00
} // case etMinecart
2013-11-10 20:48:12 +00:00
case cEntity :: etProjectile :
{
2015-03-22 19:46:08 +01:00
auto & Projectile = reinterpret_cast < const cProjectileEntity &> ( a_Entity );
2014-02-26 23:29:14 +00:00
switch ( Projectile . GetProjectileKind ())
2013-11-10 20:48:12 +00:00
{
2014-02-26 23:29:14 +00:00
case cProjectileEntity :: pkArrow :
{
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0x10 );
a_Pkt . WriteBEUInt8 ( reinterpret_cast < const cArrowEntity &> ( a_Entity ). IsCritical () ? 1 : 0 );
2014-02-26 23:29:14 +00: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 &> ( a_Entity ). GetItem ());
2014-02-26 23:29:14 +00:00
break ;
}
default : break ;
2013-11-10 20:48:12 +00:00
}
break ;
2015-03-22 19:46:08 +01:00
} // case etProjectile
2013-11-04 21:20:36 +01:00
case cEntity :: etMonster :
{
2015-03-22 19:46:08 +01:00
WriteMobMetadata ( a_Pkt , reinterpret_cast < const cMonster &> ( a_Entity ));
2013-11-04 21:20:36 +01:00
break ;
2015-03-22 19:46:08 +01:00
} // case etMonster
2014-02-17 23:00:03 +00: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 ( 0xa2 );
2015-03-22 19:46:08 +01:00
WriteItem ( a_Pkt , Frame . GetItem ());
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0x03 );
a_Pkt . WriteBEUInt8 ( Frame . GetItemRotation () / 2 );
2015-03-22 19:46:08 +01:00
break ;
} // case etItemFrame
default :
{
2014-02-17 23:00:03 +00:00
break ;
}
2015-03-22 19:46:08 +01:00
} // switch (EntityType)
2013-11-04 21:20:36 +01:00
}
2015-03-22 19:46:08 +01:00
void cProtocol172 :: WriteMobMetadata ( cPacketizer & a_Pkt , const cMonster & a_Mob )
2013-11-04 21:20:36 +01:00
{
2015-07-13 22:25:40 +02:00
// Living Enitiy Metadata
if ( a_Mob . HasCustomName ())
{
a_Pkt . WriteBEUInt8 ( 0x8a );
a_Pkt . WriteString ( a_Mob . GetCustomName ());
a_Pkt . WriteBEUInt8 ( 0x0b );
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
2013-11-08 20:56:19 +01:00
switch ( a_Mob . GetMobType ())
{
2014-09-17 18:40:10 +01:00
case mtBat :
2013-11-08 20:56:19 +01:00
{
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0x10 );
a_Pkt . WriteBEUInt8 ( reinterpret_cast < const cBat &> ( a_Mob ). IsHanging () ? 1 : 0 );
2013-11-08 20:56:19 +01:00
break ;
2015-03-22 19:46:08 +01:00
} // case mtBat
2016-02-05 23:45:45 +02:00
2015-03-22 19:46:08 +01:00
case mtCreeper :
2013-11-08 20:56:19 +01:00
{
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0x10 );
2015-11-09 19:50:40 +01:00
a_Pkt . WriteBEUInt8 ( reinterpret_cast < const cCreeper &> ( a_Mob ). IsBlowing () ? 1 : 255 );
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0x11 );
a_Pkt . WriteBEUInt8 ( reinterpret_cast < const cCreeper &> ( a_Mob ). IsCharged () ? 1 : 0 );
2013-11-08 20:56:19 +01:00
break ;
2015-03-22 19:46:08 +01:00
} // case mtCreeper
2016-02-05 23:45:45 +02:00
2015-03-22 19:46:08 +01:00
case mtEnderman :
2013-11-08 20:56:19 +01: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 ( 0x10 );
2015-07-29 09:04:03 -06:00
a_Pkt . WriteBEUInt8 ( static_cast < UInt8 > ( Enderman . GetCarriedBlock ()));
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0x11 );
2015-07-29 09:04:03 -06:00
a_Pkt . WriteBEUInt8 ( static_cast < UInt8 > ( Enderman . GetCarriedMeta ()));
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0x12 );
a_Pkt . WriteBEUInt8 ( Enderman . IsScreaming () ? 1 : 0 );
2013-11-08 20:56:19 +01:00
break ;
2015-03-22 19:46:08 +01:00
} // case mtEnderman
2016-02-05 23:45:45 +02:00
2014-09-17 18:40:10 +01:00
case mtGhast :
2013-11-08 20:56:19 +01:00
{
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0x10 );
a_Pkt . WriteBEUInt8 ( reinterpret_cast < const cGhast &> ( a_Mob ). IsCharging ());
2013-11-08 20:56:19 +01:00
break ;
2015-03-22 19:46:08 +01:00
} // case mtGhast
2016-02-05 23:45:45 +02:00
2015-03-22 19:46:08 +01:00
case mtHorse :
2013-11-08 20:56:19 +01:00
{
2015-03-22 19:46:08 +01:00
auto & Horse = reinterpret_cast < const cHorse &> ( a_Mob );
int Flags = 0 ;
if ( Horse . IsTame ())
2013-11-08 20:56:19 +01:00
{
2015-03-22 19:46:08 +01:00
Flags |= 0x02 ;
2013-11-08 20:56:19 +01:00
}
2015-03-22 19:46:08 +01:00
if ( Horse . IsSaddled ())
2013-11-08 20:56:19 +01:00
{
2015-03-22 19:46:08 +01:00
Flags |= 0x04 ;
2013-11-08 20:56:19 +01:00
}
2015-03-22 19:46:08 +01:00
if ( Horse . IsChested ())
2013-11-08 20:56:19 +01:00
{
2015-03-22 19:46:08 +01:00
Flags |= 0x08 ;
2013-11-08 20:56:19 +01:00
}
2015-03-22 19:46:08 +01:00
if ( Horse . IsBaby ())
{
Flags |= 0x10 ;
}
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
a_Pkt . WriteBEUInt8 ( static_cast < Byte > ( Horse . GetHorseType ()));
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 ());
2013-11-08 20:56:19 +01:00
break ;
2015-03-22 19:46:08 +01:00
} // case mtHorse
case mtMagmaCube :
{
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0x10 );
a_Pkt . WriteBEUInt8 ( static_cast < Byte > ( reinterpret_cast < const cMagmaCube &> ( a_Mob ). GetSize ()));
2015-03-22 19:46:08 +01:00
break ;
} // case mtMagmaCube
2016-02-05 23:45:45 +02:00
2015-03-22 19:46:08 +01:00
case mtPig :
{
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0x10 );
a_Pkt . WriteBEUInt8 ( reinterpret_cast < const cPig &> ( a_Mob ). IsSaddled () ? 1 : 0 );
2015-03-22 19:46:08 +01:00
break ;
} // case mtPig
2016-02-05 23:45:45 +02:00
2014-09-17 18:40:10 +01:00
case mtSheep :
2013-11-08 20:56:19 +01:00
{
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0x10 );
2015-03-22 19:46:08 +01:00
auto & Sheep = reinterpret_cast < const cSheep &> ( a_Mob );
Byte SheepMetadata = static_cast < Byte > ( Sheep . GetFurColor () & 0x0f );
if ( Sheep . IsSheared ())
2013-11-08 20:56:19 +01:00
{
SheepMetadata |= 0x10 ;
}
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( SheepMetadata );
2013-11-08 20:56:19 +01:00
break ;
2015-03-22 19:46:08 +01:00
} // case mtSheep
2016-02-05 23:45:45 +02:00
2014-09-17 18:40:10 +01:00
case mtSkeleton :
2013-11-08 20:56:19 +01:00
{
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0x0d );
a_Pkt . WriteBEUInt8 ( reinterpret_cast < const cSkeleton &> ( a_Mob ). IsWither () ? 1 : 0 );
2013-11-08 20:56:19 +01:00
break ;
2015-03-22 19:46:08 +01:00
} // case mtSkeleton
2016-02-05 23:45:45 +02:00
2015-03-22 19:46:08 +01:00
case mtSlime :
2013-11-08 20:56:19 +01:00
{
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0x10 );
a_Pkt . WriteBEUInt8 ( static_cast < Byte > ( reinterpret_cast < const cSlime &> ( a_Mob ). GetSize ()));
2013-11-08 20:56:19 +01:00
break ;
2015-03-22 19:46:08 +01:00
} // case mtSlime
2016-02-05 23:45:45 +02:00
2015-03-22 19:46:08 +01:00
case mtVillager :
2014-03-25 10:32:58 +02:00
{
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0x50 );
a_Pkt . WriteBEInt32 ( reinterpret_cast < const cVillager &> ( a_Mob ). GetVilType ());
2014-03-25 10:32:58 +02:00
break ;
2015-03-22 19:46:08 +01:00
} // case mtVillager
2016-02-05 23:45:45 +02:00
2015-03-22 19:46:08 +01:00
case mtWitch :
2013-11-08 20:56:19 +01:00
{
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0x15 );
a_Pkt . WriteBEUInt8 ( reinterpret_cast < const cWitch &> ( a_Mob ). IsAngry () ? 1 : 0 );
2013-11-08 20:56:19 +01:00
break ;
2015-03-22 19:46:08 +01:00
} // case mtWitch
case mtWither :
2013-11-08 20:56:19 +01:00
{
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEUInt8 ( 0x54 ); // Int at index 20
a_Pkt . WriteBEInt32 ( static_cast < int > ( reinterpret_cast < const cWither &> ( a_Mob ). GetWitherInvulnerableTicks ()));
a_Pkt . WriteBEUInt8 ( 0x66 ); // Float at index 6
a_Pkt . WriteBEFloat ( static_cast < float > ( a_Mob . GetHealth ()));
2013-11-08 20:56:19 +01:00
break ;
2015-03-22 19:46:08 +01:00
} // case mtWither
2016-02-05 23:45:45 +02:00
2015-03-22 19:46:08 +01:00
case mtWolf :
2013-11-08 20:56:19 +01:00
{
2015-03-22 19:46:08 +01:00
auto & Wolf = reinterpret_cast < const cWolf &> ( a_Mob );
Byte WolfStatus = 0 ;
if ( Wolf . IsSitting ())
2013-11-08 20:56:19 +01:00
{
2015-03-22 19:46:08 +01:00
WolfStatus |= 0x1 ;
2013-11-08 20:56:19 +01:00
}
2015-03-22 19:46:08 +01:00
if ( Wolf . IsAngry ())
2013-11-08 20:56:19 +01:00
{
2015-03-22 19:46:08 +01:00
WolfStatus |= 0x2 ;
2013-11-08 20:56:19 +01:00
}
2015-03-22 19:46:08 +01:00
if ( Wolf . IsTame ())
2013-11-08 20:56:19 +01:00
{
2015-03-22 19:46:08 +01:00
WolfStatus |= 0x4 ;
2013-11-08 20:56:19 +01: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 );
a_Pkt . WriteBEUInt8 ( static_cast < Byte > ( Wolf . GetCollarColor ()));
2013-11-08 20:56:19 +01:00
break ;
2015-03-22 19:46:08 +01:00
} // case mtWolf
2016-02-05 23:45:45 +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 );
a_Pkt . WriteBEUInt8 ( Zombie . IsBaby () ? 1 : 0 );
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-01-18 22:43:35 +01:00
default :
{
// No data to send for this mob
break ;
}
2013-11-08 20:56:19 +01:00
} // switch (a_Mob.GetType())
}
2015-03-22 19:46:08 +01:00
void cProtocol172 :: WriteEntityProperties ( cPacketizer & a_Pkt , const cEntity & a_Entity )
2013-11-08 20:56:19 +01:00
{
if ( ! a_Entity . IsMob ())
{
// No properties for anything else than mobs
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEInt32 ( 0 );
2013-11-08 20:56:19 +01:00
return ;
}
2014-02-04 23:07:22 +00:00
2015-03-22 19:46:08 +01:00
// auto & Mob = reinterpret_cast<const cMonster &>(a_Entity);
2013-11-08 20:56:19 +01:00
// TODO: Send properties and modifiers based on the mob type
2016-02-05 23:45:45 +02:00
2015-03-22 23:09:23 +01:00
a_Pkt . WriteBEInt32 ( 0 ); // NumProperties
2013-11-04 21:20:36 +01:00
}
2014-04-14 18:52:21 +02:00
2014-07-17 22:15:34 +02:00
////////////////////////////////////////////////////////////////////////////////
2014-04-14 18:52:21 +02:00
// cProtocol176:
cProtocol176 :: cProtocol176 ( cClientHandle * a_Client , const AString & a_ServerAddress , UInt16 a_ServerPort , UInt32 a_State ) :
super ( a_Client , a_ServerAddress , a_ServerPort , a_State )
{
}
void cProtocol176 :: 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-07-30 13:52:51 +02:00
Pkt . WriteString ( cMojangAPI :: MakeUUIDDashed ( a_Player . GetClientHandle () -> GetUUID ()));
2014-09-02 19:12:35 +02:00
if ( a_Player . HasCustomName ())
{
Pkt . WriteString ( a_Player . GetCustomName ());
}
else
{
Pkt . WriteString ( a_Player . GetName ());
}
2014-07-14 19:49:31 +01:00
2014-08-03 22:15:26 +02:00
const Json :: Value & Properties = a_Player . GetClientHandle () -> GetProperties ();
2015-03-22 23:09:23 +01:00
Pkt . WriteVarInt32 ( Properties . size ());
2014-07-16 12:05:09 +01:00
2015-10-19 16:03:55 +02:00
for ( auto & Node : Properties )
2014-07-14 19:49:31 +01:00
{
2015-10-19 16:03:55 +02:00
Pkt . WriteString ( Node . get ( "name" , "" ). asString ());
Pkt . WriteString ( Node . get ( "value" , "" ). asString ());
Pkt . WriteString ( Node . get ( "signature" , "" ). asString ());
2014-07-14 19:49:31 +01:00
}
2014-04-14 18:52:21 +02:00
Pkt . WriteFPInt ( a_Player . GetPosX ());
Pkt . WriteFPInt ( a_Player . GetPosY ());
Pkt . WriteFPInt ( a_Player . GetPosZ ());
Pkt . WriteByteAngle ( a_Player . GetYaw ());
Pkt . WriteByteAngle ( a_Player . GetPitch ());
short ItemType = a_Player . GetEquippedItem (). IsEmpty () ? 0 : a_Player . GetEquippedItem (). m_ItemType ;
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 ( 0x7f ); // Metadata: end
2014-04-14 18:52:21 +02:00
}
2014-04-14 22:52:59 +02:00
void cProtocol176 :: HandlePacketStatusRequest ( cByteBuffer & a_ByteBuffer )
{
2014-08-20 16:01:30 +02:00
cServer * Server = cRoot :: Get () -> GetServer ();
AString Motd = Server -> GetDescription ();
int NumPlayers = Server -> GetNumPlayers ();
int MaxPlayers = Server -> GetMaxPlayers ();
AString Favicon = Server -> GetFaviconData ();
2014-08-20 22:19:50 +02:00
cRoot :: Get () -> GetPluginManager () -> CallHookServerPing ( * m_Client , Motd , NumPlayers , MaxPlayers , Favicon );
2014-08-20 16:01:30 +02:00
// Version:
Json :: Value Version ;
2015-09-25 10:14:17 +02:00
Version [ "name" ] = "Cuberite 1.7.6" ;
2014-08-20 16:01:30 +02:00
Version [ "protocol" ] = 5 ;
// Players:
Json :: Value Players ;
Players [ "online" ] = NumPlayers ;
Players [ "max" ] = MaxPlayers ;
// TODO: Add "sample"
// Description:
Json :: Value Description ;
Description [ "text" ] = Motd . 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 );
2014-04-14 22:52:59 +02:00
cPacketizer Pkt ( * this , 0x00 ); // Response packet
Pkt . WriteString ( Response );
}