
// Protocol125.cpp

// Implements the cProtocol125 class representing the release 1.2.5 protocol (#29)
/*
Documentation:
	- protocol: http://wiki.vg/wiki/index.php?title=Protocol&oldid=2513
	- session handling: http://wiki.vg/wiki/index.php?title=Session&oldid=2262
	- slot format: http://wiki.vg/wiki/index.php?title=Slot_Data&oldid=2152
*/

#include "Globals.h"

#include "Protocol125.h"

#include "../ClientHandle.h"
#include "../World.h"
#include "ChunkDataSerializer.h"
#include "../Entities/Entity.h"
#include "../Entities/ExpOrb.h"
#include "../Mobs/Monster.h"
#include "../Entities/Pickup.h"
#include "../Entities/Player.h"
#include "../ChatColor.h"
#include "../UI/Window.h"
#include "../Root.h"
#include "../Server.h"

#include "../Entities/ProjectileEntity.h"
#include "../Entities/Minecart.h"
#include "../Entities/FallingBlock.h"

#include "../Mobs/IncludeAllMonsters.h"

#include "../CompositeChat.h"





enum
{
	PACKET_KEEP_ALIVE                = 0x00,
	PACKET_LOGIN                     = 0x01,
	PACKET_HANDSHAKE                 = 0x02,
	PACKET_CHAT                      = 0x03,
	PACKET_UPDATE_TIME               = 0x04,
	PACKET_ENTITY_EQUIPMENT          = 0x05,
	PACKET_USE_ENTITY                = 0x07,
	PACKET_UPDATE_HEALTH             = 0x08,
	PACKET_RESPAWN                   = 0x09,
	PACKET_PLAYER_ON_GROUND          = 0x0a,
	PACKET_PLAYER_POS                = 0x0b,
	PACKET_PLAYER_LOOK               = 0x0c,
	PACKET_PLAYER_MOVE_LOOK          = 0x0d,
	PACKET_BLOCK_DIG                 = 0x0e,
	PACKET_BLOCK_PLACE               = 0x0f,
	PACKET_SLOT_SELECTED             = 0x10,
	PACKET_USE_BED                   = 0x11,
	PACKET_ANIMATION                 = 0x12,
	PACKET_PACKET_ENTITY_ACTION      = 0x13,
	PACKET_PLAYER_SPAWN              = 0x14,
	PACKET_PICKUP_SPAWN              = 0x15,
	PACKET_COLLECT_PICKUP            = 0x16,
	PACKET_SPAWN_OBJECT              = 0x17,
	PACKET_SPAWN_MOB                 = 0x18,
	PACKET_ENTITY_VELOCITY           = 0x1c,
	PACKET_DESTROY_ENTITY            = 0x1d,
	PACKET_ENTITY                    = 0x1e,
	PACKET_ENT_REL_MOVE              = 0x1f,
	PACKET_ENT_LOOK                  = 0x20,
	PACKET_ENT_REL_MOVE_LOOK         = 0x21,
	PACKET_ENT_TELEPORT              = 0x22,
	PACKET_ENT_HEAD_LOOK             = 0x23,
	PACKET_ENT_STATUS                = 0x26,
	PACKET_ATTACH_ENTITY             = 0x27,
	PACKET_METADATA                  = 0x28,
	PACKET_ENTITY_EFFECT             = 0x29,
	PACKET_SPAWN_EXPERIENCE_ORB      = 0x1A,
	PACKET_REMOVE_ENTITY_EFFECT      = 0x2a,
	PACKET_EXPERIENCE                = 0x2b,
	PACKET_PRE_CHUNK                 = 0x32,
	PACKET_MAP_CHUNK                 = 0x33,
	PACKET_MULTI_BLOCK               = 0x34,
	PACKET_BLOCK_CHANGE              = 0x35,
	PACKET_BLOCK_ACTION              = 0x36,
	PACKET_EXPLOSION                 = 0x3C,
	PACKET_SOUND_EFFECT              = 0x3e,
	PACKET_SOUND_PARTICLE_EFFECT     = 0x3d,
	PACKET_CHANGE_GAME_STATE         = 0x46,
	PACKET_THUNDERBOLT               = 0x47,
	PACKET_WINDOW_OPEN               = 0x64,
	PACKET_WINDOW_CLOSE              = 0x65,
	PACKET_WINDOW_CLICK              = 0x66,
	PACKET_INVENTORY_SLOT            = 0x67,
	PACKET_INVENTORY_WHOLE           = 0x68,
	PACKET_WINDOW_PROPERTY           = 0x69,
	PACKET_CREATIVE_INVENTORY_ACTION = 0x6B,
	PACKET_UPDATE_SIGN               = 0x82,
	PACKET_ITEM_DATA                 = 0x83,
	PACKET_PLAYER_LIST_ITEM          = 0xC9,
	PACKET_PLAYER_ABILITIES          = 0xca,
	PACKET_PLUGIN_MESSAGE            = 0xfa,
	PACKET_PING                      = 0xfe,
	PACKET_DISCONNECT                = 0xff
} ;





#define HANDLE_PACKET_READ(Proc, Type, Var) \
	Type Var; \
	{ \
		if (!m_ReceivedData.Proc(Var)) \
		{ \
			m_ReceivedData.CheckValid(); \
			return PARSE_INCOMPLETE; \
		} \
		m_ReceivedData.CheckValid(); \
	}




typedef unsigned char Byte;





cProtocol125::cProtocol125(cClientHandle * a_Client) :
	super(a_Client),
	m_ReceivedData(32 KiB)
{
}





void cProtocol125::SendAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle)
{
	cCSLock Lock(m_CSPacket);
	WriteByte(PACKET_ATTACH_ENTITY);
	WriteInt(a_Entity.GetUniqueID());
	WriteInt((a_Vehicle == NULL) ? -1 : a_Vehicle->GetUniqueID());
	Flush();
}





void cProtocol125::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType)
{
	UNUSED(a_BlockType);
	
	cCSLock Lock(m_CSPacket);
	WriteByte (PACKET_BLOCK_ACTION);
	WriteInt  (a_BlockX);
	WriteShort((short)a_BlockY);
	WriteInt  (a_BlockZ);
	WriteChar (a_Byte1);
	WriteChar (a_Byte2);
	Flush();
}





void cProtocol125::SendBlockBreakAnim(int a_entityID, int a_BlockX, int a_BlockY, int a_BlockZ, char stage)
{
	// Not supported in this protocol version
}





void cProtocol125::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
{
	cCSLock Lock(m_CSPacket);
	WriteByte(PACKET_BLOCK_CHANGE);
	WriteInt (a_BlockX);
	WriteByte((unsigned char)a_BlockY);
	WriteInt (a_BlockZ);
	WriteByte(a_BlockType);
	WriteByte(a_BlockMeta);
	Flush();
}





void cProtocol125::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes)
{
	cCSLock Lock(m_CSPacket);
	if (a_Changes.size() == 1)
	{
		// Special packet for single-block changes
		const sSetBlock & blk = a_Changes.front();
		SendBlockChange(a_ChunkX * cChunkDef::Width + blk.x, blk.y, a_ChunkZ * cChunkDef::Width + blk.z, blk.BlockType, blk.BlockMeta);
		return;
	}
	
	WriteByte (PACKET_MULTI_BLOCK);
	WriteInt  (a_ChunkX);
	WriteInt  (a_ChunkZ);
	WriteShort((short)a_Changes.size());
	WriteUInt ((UInt32)(4 * a_Changes.size()));
	for (sSetBlockVector::const_iterator itr = a_Changes.begin(), end = a_Changes.end(); itr != end; ++itr)
	{
		UInt32 Coords = ((UInt32)itr->y) | ((UInt32)(itr->z << 8)) | ((UInt32)(itr->x << 12));
		UInt32 Blocks = ((UInt32)itr->BlockMeta) | ((UInt32)(itr->BlockType << 4));
		WriteUInt(Coords << 16 | Blocks);
	}
	Flush();
}





void cProtocol125::SendChat(const AString & a_Message)
{
	cCSLock Lock(m_CSPacket);
	WriteByte  (PACKET_CHAT);
	WriteString(a_Message);
	Flush();
}





void cProtocol125::SendChat(const cCompositeChat & a_Message)
{
	// This version doesn't support composite messages, just extract each part's text and use it:
	
	// Send the message:
	cCSLock Lock(m_CSPacket);
	WriteByte  (PACKET_CHAT);
	WriteString(a_Message.ExtractText());
	Flush();
}





void cProtocol125::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer)
{
	cCSLock Lock(m_CSPacket);

	// Send the pre-chunk:
	SendPreChunk(a_ChunkX, a_ChunkZ, true);
	
	// Send the chunk data:
	AString Serialized = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_2_5);
	WriteByte(PACKET_MAP_CHUNK);
	WriteInt (a_ChunkX);
	WriteInt (a_ChunkZ);
	SendData(Serialized.data(), Serialized.size());
	Flush();
}





void cProtocol125::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player)
{
	cCSLock Lock(m_CSPacket);
	WriteByte(PACKET_COLLECT_PICKUP);
	WriteInt (a_Pickup.GetUniqueID());
	WriteInt (a_Player.GetUniqueID());
	Flush();
}





void cProtocol125::SendDestroyEntity(const cEntity & a_Entity)
{
	cCSLock Lock(m_CSPacket);
	WriteByte(PACKET_DESTROY_ENTITY);
	WriteInt (a_Entity.GetUniqueID());
	Flush();
}





void cProtocol125::SendDisconnect(const AString & a_Reason)
{
	cCSLock Lock(m_CSPacket);
	WriteByte  ((unsigned char)PACKET_DISCONNECT);
	WriteString(a_Reason);
	Flush();
}





void cProtocol125::SendEditSign(int a_BlockX, int a_BlockY, int a_BlockZ)
{
	// This protocol version doesn't support this packet, sign editor is invoked by the client automatically
	UNUSED(a_BlockX);
	UNUSED(a_BlockY);
	UNUSED(a_BlockZ);
}





void cProtocol125::SendEntityEffect(const cEntity & a_Entity, int a_EffectID, int a_Amplifier, short a_Duration)
{
	cCSLock Lock(m_CSPacket);
	WriteByte (PACKET_ENTITY_EFFECT);
	WriteInt  (a_Entity.GetUniqueID());
	WriteByte ((Byte)a_EffectID);
	WriteByte ((Byte)a_Amplifier);
	WriteShort(a_Duration);
	Flush();
}





void cProtocol125::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item)
{
	cCSLock Lock(m_CSPacket);
	WriteByte (PACKET_ENTITY_EQUIPMENT);
	WriteInt  (a_Entity.GetUniqueID());
	WriteShort(a_SlotNum);
	WriteShort(a_Item.m_ItemType);
	WriteShort(a_Item.m_ItemDamage);
	Flush();
}





void cProtocol125::SendEntityHeadLook(const cEntity & a_Entity)
{
	ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID());  // Must not send for self
	
	cCSLock Lock(m_CSPacket);
	WriteByte(PACKET_ENT_HEAD_LOOK);
	WriteInt (a_Entity.GetUniqueID());
	WriteChar((char)((a_Entity.GetHeadYaw() / 360.f) * 256));
	Flush();
}





void cProtocol125::SendEntityLook(const cEntity & a_Entity)
{
	ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID());  // Must not send for self
	
	cCSLock Lock(m_CSPacket);
	WriteByte(PACKET_ENT_LOOK);
	WriteInt (a_Entity.GetUniqueID());
	WriteChar((char)((a_Entity.GetYaw()   / 360.f) * 256));
	WriteChar((char)((a_Entity.GetPitch() / 360.f) * 256));
	Flush();
}





void cProtocol125::SendEntityMetadata(const cEntity & a_Entity)
{
	cCSLock Lock(m_CSPacket);
	WriteByte(PACKET_METADATA);
	WriteInt (a_Entity.GetUniqueID());
	
	WriteCommonMetadata(a_Entity);
	if (a_Entity.IsMob())
	{
		WriteMobMetadata(((const cMonster &)a_Entity));
	}
	else
	{
		WriteEntityMetadata(a_Entity);
	}	
	WriteByte(0x7f);

	Flush();
}





void cProtocol125::SendEntityProperties(const cEntity & a_Entity)
{
	// Not supported in this protocol version
}





void cProtocol125::SendEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ)
{
	ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID());  // Must not send for self
	
	cCSLock Lock(m_CSPacket);
	WriteByte(PACKET_ENT_REL_MOVE);
	WriteInt (a_Entity.GetUniqueID());
	WriteChar(a_RelX);
	WriteChar(a_RelY);
	WriteChar(a_RelZ);
	Flush();
}





void cProtocol125::SendEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ)
{
	ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID());  // Must not send for self
	
	cCSLock Lock(m_CSPacket);
	WriteByte(PACKET_ENT_REL_MOVE_LOOK);
	WriteInt (a_Entity.GetUniqueID());
	WriteChar(a_RelX);
	WriteChar(a_RelY);
	WriteChar(a_RelZ);
	WriteChar((char)((a_Entity.GetYaw()   / 360.f) * 256));
	WriteChar((char)((a_Entity.GetPitch() / 360.f) * 256));
	Flush();
}





void cProtocol125::SendEntityStatus(const cEntity & a_Entity, char a_Status)
{
	cCSLock Lock(m_CSPacket);
	WriteByte(PACKET_ENT_STATUS);
	WriteInt (a_Entity.GetUniqueID());
	WriteChar(a_Status);
	Flush();
}





void cProtocol125::SendEntityVelocity(const cEntity & a_Entity)
{
	ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID());  // Must not send for self
	
	cCSLock Lock(m_CSPacket);
	WriteByte(PACKET_ENTITY_VELOCITY);
	WriteInt (a_Entity.GetUniqueID());
	WriteShort((short) (a_Entity.GetSpeedX() * 400)); //400 = 8000 / 20 
	WriteShort((short) (a_Entity.GetSpeedY() * 400));
	WriteShort((short) (a_Entity.GetSpeedZ() * 400));
	Flush();
}





void cProtocol125::SendExplosion(double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion)
{
	cCSLock Lock(m_CSPacket);
	WriteByte(PACKET_EXPLOSION);
	WriteDouble (a_BlockX);
	WriteDouble (a_BlockY);
	WriteDouble (a_BlockZ);
	WriteFloat  (a_Radius);
	WriteInt    ((Int32)a_BlocksAffected.size());
	int BlockX = (int)a_BlockX;
	int BlockY = (int)a_BlockY;
	int BlockZ = (int)a_BlockZ;
	for (cVector3iArray::const_iterator itr = a_BlocksAffected.begin(); itr != a_BlocksAffected.end(); ++itr)
	{
		WriteByte((Byte)(itr->x - BlockX));
		WriteByte((Byte)(itr->y - BlockY));
		WriteByte((Byte)(itr->z - BlockZ));
	}
	WriteFloat((float)a_PlayerMotion.x);
	WriteFloat((float)a_PlayerMotion.y);
	WriteFloat((float)a_PlayerMotion.z);
	Flush();
}





void cProtocol125::SendGameMode(eGameMode a_GameMode)
{
	cCSLock Lock(m_CSPacket);
	WriteByte(PACKET_CHANGE_GAME_STATE);
	WriteByte(3);
	WriteChar((char)a_GameMode);
	Flush();
}





void cProtocol125::SendHandshake(const AString & a_ConnectionHash)
{
	cCSLock Lock(m_CSPacket);
	WriteByte  (PACKET_HANDSHAKE);
	WriteString(a_ConnectionHash);
	Flush();
}





void cProtocol125::SendHealth(void)
{
	cCSLock Lock(m_CSPacket);
	WriteByte (PACKET_UPDATE_HEALTH);
	WriteShort((short)m_Client->GetPlayer()->GetHealth());
	WriteShort((short)m_Client->GetPlayer()->GetFoodLevel());
	WriteFloat((float)m_Client->GetPlayer()->GetFoodSaturationLevel());
	Flush();
}





void cProtocol125::SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem & a_Item)
{
	cCSLock Lock(m_CSPacket);
	WriteByte (PACKET_INVENTORY_SLOT);
	WriteChar (a_WindowID);
	WriteShort(a_SlotNum);
	WriteItem (a_Item);
	Flush();
}





void cProtocol125::SendKeepAlive(int a_PingID)
{
	cCSLock Lock(m_CSPacket);
	WriteByte(PACKET_KEEP_ALIVE);
	WriteInt (a_PingID);
	Flush();
}





void cProtocol125::SendLogin(const cPlayer & a_Player, const cWorld & a_World)
{
	UNUSED(a_World);
	cCSLock Lock(m_CSPacket);

	WriteByte  (PACKET_LOGIN);
	WriteInt   (a_Player.GetUniqueID());  // EntityID of the player
	WriteString("");  // Username, not used
	WriteString("default");  // Level type
	WriteInt   ((int)a_Player.GetGameMode());
	WriteInt   ((int)(a_World.GetDimension()));
	WriteByte  (2);  // TODO: Difficulty
	WriteByte  (0);  // Unused
	WriteByte  (60);  // Client list width or something
	Flush();
}





void cProtocol125::SendMapColumn(int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length)
{
	cCSLock Lock(m_CSPacket);

	WriteByte (PACKET_ITEM_DATA);
	WriteShort(E_ITEM_MAP);
	WriteShort((short)a_ID);
	WriteShort((short)(3 + a_Length));

	WriteByte(0);
	WriteChar((char)a_X);
	WriteChar((char)a_Y);
	
	SendData((const char *)a_Colors, a_Length);

	Flush();
}





void cProtocol125::SendMapDecorators(int a_ID, const cMapDecoratorList & a_Decorators)
{
	cCSLock Lock(m_CSPacket);

	WriteByte (PACKET_ITEM_DATA);
	WriteShort(E_ITEM_MAP);
	WriteShort((short)a_ID);
	WriteShort((short)(1 + (3 * a_Decorators.size())));

	WriteByte(1);
	
	for (cMapDecoratorList::const_iterator it = a_Decorators.begin(); it != a_Decorators.end(); ++it)
	{
		WriteByte((Byte)(it->GetType() << 4) | (it->GetRot() & 0xf));
		WriteByte((Byte)it->GetPixelX());
		WriteByte((Byte)it->GetPixelZ());
	}

	Flush();
}






void cProtocol125::SendPickupSpawn(const cPickup & a_Pickup)
{
	cCSLock Lock(m_CSPacket);
	WriteByte   (PACKET_PICKUP_SPAWN);
	WriteInt    (a_Pickup.GetUniqueID());
	WriteShort  (a_Pickup.GetItem().m_ItemType);
	WriteChar   (a_Pickup.GetItem().m_ItemCount);
	WriteShort  (a_Pickup.GetItem().m_ItemDamage);
	WriteVectorI((Vector3i)(a_Pickup.GetPosition() * 32));
	WriteByte   ((char)(a_Pickup.GetSpeed().x * 8));
	WriteByte   ((char)(a_Pickup.GetSpeed().y * 8));
	WriteByte   ((char)(a_Pickup.GetSpeed().z * 8));
	Flush();
}





void cProtocol125::SendEntityAnimation(const cEntity & a_Entity, char a_Animation)
{
	cCSLock Lock(m_CSPacket);
	WriteByte(PACKET_ANIMATION);
	WriteInt (a_Entity.GetUniqueID());
	WriteChar(a_Animation);
	Flush();
}





void cProtocol125::SendParticleEffect(const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount)
{
	// Not supported by this protocol version
}





void cProtocol125::SendPlayerListItem(const cPlayer & a_Player, bool a_IsOnline)
{
	cCSLock Lock(m_CSPacket);
	AString PlayerName(a_Player.GetColor());
	PlayerName.append(a_Player.GetName());
	if (PlayerName.length() > 14)
	{
		PlayerName.erase(14);
	}
	PlayerName += cChatColor::White;

	WriteByte  ((unsigned char)PACKET_PLAYER_LIST_ITEM);
	WriteString(PlayerName);
	WriteBool  (a_IsOnline);
	WriteShort (a_IsOnline ? a_Player.GetClientHandle()->GetPing() : 0);
	Flush();
}





void cProtocol125::SendPlayerMaxSpeed(void)
{
	// Not supported by this protocol version
}





void cProtocol125::SendPlayerMoveLook(void)
{
	cCSLock Lock(m_CSPacket);

	/*
	LOGD("Sending PlayerMoveLook: {%0.2f, %0.2f, %0.2f}, stance %0.2f, OnGround: %d",
		m_Player->GetPosX(), m_Player->GetPosY(), m_Player->GetPosZ(), m_Player->GetStance(), m_Player->IsOnGround() ? 1 : 0
	);
	*/

	WriteByte	(PACKET_PLAYER_MOVE_LOOK);
	cPlayer * Player = m_Client->GetPlayer();
	WriteDouble(Player->GetPosX());
	WriteDouble(Player->GetStance() + 0.03);  // Add a small amount so that the player doesn't start inside a block
	WriteDouble(Player->GetPosY()   + 0.03);  // Add a small amount so that the player doesn't start inside a block
	WriteDouble(Player->GetPosZ());
	WriteFloat ((float)(Player->GetYaw()));
	WriteFloat ((float)(Player->GetPitch()));
	WriteBool  (Player->IsOnGround());
	Flush();
}





void cProtocol125::SendPlayerPosition(void)
{
	cCSLock Lock(m_CSPacket);
	LOGD("Ignore send PlayerPos");  // PlayerPos is a C->S packet only now
}





void cProtocol125::SendPlayerSpawn(const cPlayer & a_Player)
{
	const cItem & HeldItem = a_Player.GetEquippedItem();
	cCSLock Lock(m_CSPacket);
	WriteByte	 (PACKET_PLAYER_SPAWN);
	WriteInt   (a_Player.GetUniqueID());
	WriteString(a_Player.GetName());
	WriteInt   ((int)(a_Player.GetPosX() * 32));
	WriteInt   ((int)(a_Player.GetPosY() * 32));
	WriteInt   ((int)(a_Player.GetPosZ() * 32));
	WriteChar  ((char)((a_Player.GetYaw()   / 360.f) * 256));
	WriteChar  ((char)((a_Player.GetPitch() / 360.f) * 256));
	WriteShort (HeldItem.IsEmpty() ? 0 : HeldItem.m_ItemType);
	Flush();
}





void cProtocol125::SendPluginMessage(const AString & a_Channel, const AString & a_Message)
{
	cCSLock Lock(m_CSPacket);
	WriteByte(PACKET_PLUGIN_MESSAGE);
	WriteString(a_Channel);
	WriteShort((short)a_Message.size());
	SendData(a_Message.data(), a_Message.size());
	Flush();
}





void cProtocol125::SendRemoveEntityEffect(const cEntity & a_Entity, int a_EffectID)
{
	cCSLock Lock(m_CSPacket);
	WriteByte(PACKET_REMOVE_ENTITY_EFFECT);
	WriteInt (a_Entity.GetUniqueID());
	WriteChar((char)a_EffectID);
	Flush();
}





void cProtocol125::SendRespawn(void)
{
	cCSLock Lock(m_CSPacket);
	WriteByte  (PACKET_RESPAWN);
	WriteInt   ((int)(m_Client->GetPlayer()->GetWorld()->GetDimension()));
	WriteByte  (2);  // TODO: Difficulty; 2 = Normal
	WriteChar  ((char)m_Client->GetPlayer()->GetGameMode());
	WriteShort (256);  // Current world height
	WriteString("default");
}





void cProtocol125::SendExperience(void)
{
	cCSLock Lock(m_CSPacket);
	WriteByte  (PACKET_EXPERIENCE);
	WriteFloat (m_Client->GetPlayer()->GetXpPercentage());
	WriteShort (m_Client->GetPlayer()->GetXpLevel());
	WriteShort (m_Client->GetPlayer()->GetCurrentXp());
	Flush();
}





void cProtocol125::SendExperienceOrb(const cExpOrb & a_ExpOrb)
{
	cCSLock Lock(m_CSPacket);
	WriteByte(PACKET_SPAWN_EXPERIENCE_ORB);
	WriteInt(a_ExpOrb.GetUniqueID());
	WriteInt((int) a_ExpOrb.GetPosX());
	WriteInt((int) a_ExpOrb.GetPosY());
	WriteInt((int) a_ExpOrb.GetPosZ());
	WriteShort((short)a_ExpOrb.GetReward());
	Flush();
}





void cProtocol125::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch)
{
	// Not needed in this protocol version
}





void cProtocol125::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data)
{
	// Not implemented in this protocol version
}





void cProtocol125::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock)
{
	// This protocol version implements falling blocks using the spawn object / vehicle packet:
	SendSpawnObject(a_FallingBlock, 70, a_FallingBlock.GetBlockType(), 0, 0);
}





void cProtocol125::SendSpawnMob(const cMonster & a_Mob)
{
	cCSLock Lock(m_CSPacket);
	WriteByte   (PACKET_SPAWN_MOB);
	WriteInt    (a_Mob.GetUniqueID());
	WriteByte   ((Byte)a_Mob.GetMobType());
	WriteVectorI((Vector3i)(a_Mob.GetPosition() * 32));
	WriteByte   (0);
	WriteByte   (0);
	WriteByte   (0);

	WriteCommonMetadata(a_Mob);
	WriteMobMetadata(a_Mob);
	WriteByte(0x7f);

	Flush();
}





void cProtocol125::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch)
{
	UNUSED(a_Yaw);
	UNUSED(a_Pitch);
	
	cCSLock Lock(m_CSPacket);
	WriteByte(PACKET_SPAWN_OBJECT);
	WriteInt (a_Entity.GetUniqueID());
	WriteChar(a_ObjectType);
	WriteInt ((int)(a_Entity.GetPosX() * 32));
	WriteInt ((int)(a_Entity.GetPosY() * 32));
	WriteInt ((int)(a_Entity.GetPosZ() * 32));
	WriteByte(a_Pitch);
	WriteByte(a_Yaw);
	WriteInt (a_ObjectData);
	if (a_ObjectData != 0)
	{
		WriteShort((short)(a_Entity.GetSpeedX() * 400));
		WriteShort((short)(a_Entity.GetSpeedY() * 400));
		WriteShort((short)(a_Entity.GetSpeedZ() * 400));
	}
	Flush();
}





void cProtocol125::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType)
{
	cCSLock Lock(m_CSPacket);
	WriteByte (PACKET_SPAWN_OBJECT);
	WriteInt  (a_Vehicle.GetUniqueID());
	WriteChar (a_VehicleType);
	WriteInt  ((int)(a_Vehicle.GetPosX() * 32));
	WriteInt  ((int)(a_Vehicle.GetPosY() * 32));
	WriteInt  ((int)(a_Vehicle.GetPosZ() * 32));
	WriteByte ((Byte)((a_Vehicle.GetPitch() / 360.f) * 256));
	WriteByte ((Byte)((a_Vehicle.GetYaw()   / 360.f) * 256));
	WriteInt  (a_VehicleSubType);
	if (a_VehicleSubType != 0)
	{
		WriteShort((short)(a_Vehicle.GetSpeedX() * 400));
		WriteShort((short)(a_Vehicle.GetSpeedY() * 400));
		WriteShort((short)(a_Vehicle.GetSpeedZ() * 400));
	}
	Flush();
}





void cProtocol125::SendTabCompletionResults(const AStringVector & a_Results)
{
	// This protocol version doesn't support tab completion
	UNUSED(a_Results);
}





void cProtocol125::SendTeleportEntity(const cEntity & a_Entity)
{
	cCSLock Lock(m_CSPacket);
	WriteByte   (PACKET_ENT_TELEPORT);
	WriteInt    (a_Entity.GetUniqueID());
	WriteInt    ((int)(floor(a_Entity.GetPosX() * 32)));
	WriteInt    ((int)(floor(a_Entity.GetPosY() * 32)));
	WriteInt    ((int)(floor(a_Entity.GetPosZ() * 32)));
	WriteChar   ((char)((a_Entity.GetYaw() / 360.f) * 256));
	WriteChar   ((char)((a_Entity.GetPitch() / 360.f) * 256));
	Flush();
}





void cProtocol125::SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ)
{
	cCSLock Lock(m_CSPacket);
	WriteByte(PACKET_THUNDERBOLT);
	WriteInt (0x7fffffff);  // Entity ID of the thunderbolt; we use a constant one
	WriteBool(true);  // Unknown bool
	WriteInt (a_BlockX * 32);
	WriteInt (a_BlockY * 32);
	WriteInt (a_BlockZ * 32);
	Flush();
}





void cProtocol125::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay)
{
	cCSLock Lock(m_CSPacket);
	WriteByte (PACKET_UPDATE_TIME);
	// Use a_WorldAge for daycount, and a_TimeOfDay for the proper time of day:
	WriteInt64((24000 * (a_WorldAge / 24000)) + (a_TimeOfDay % 24000));
	Flush();
}





void cProtocol125::SendUnloadChunk(int a_ChunkX, int a_ChunkZ)
{
	cCSLock Lock(m_CSPacket);
	SendPreChunk(a_ChunkX, a_ChunkZ, false);
}





void cProtocol125::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
)
{
	cCSLock Lock(m_CSPacket);
	WriteByte  ((unsigned char)PACKET_UPDATE_SIGN);
	WriteInt   (a_BlockX);
	WriteShort ((short)a_BlockY);
	WriteInt   (a_BlockZ);
	WriteString(a_Line1);
	WriteString(a_Line2);
	WriteString(a_Line3);
	WriteString(a_Line4);
	Flush();
}





void cProtocol125::SendUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ )
{
	cCSLock Lock(m_CSPacket);
	WriteByte(PACKET_USE_BED);
	WriteInt (a_Entity.GetUniqueID());
	WriteByte(0);	// Unknown byte only 0 has been observed
	WriteInt (a_BlockX);
	WriteByte((Byte)a_BlockY);
	WriteInt (a_BlockZ);
	Flush();
}





void cProtocol125::SendWeather(eWeather a_Weather)
{
	cCSLock Lock(m_CSPacket);
	switch( a_Weather )
	{
		case eWeather_Sunny:
		{
			WriteByte(PACKET_CHANGE_GAME_STATE);
			WriteByte(2);  // Stop rain
			WriteByte(0);  // Unused
			Flush();
			break;
		}
		
		case eWeather_Rain:
		case eWeather_ThunderStorm:
		{
			WriteByte(PACKET_CHANGE_GAME_STATE);
			WriteByte(1);  // Begin rain
			WriteByte(0);  // Unused
			Flush();
			break;
		}
	}
}





void cProtocol125::SendWholeInventory(const cWindow & a_Window)
{
	cCSLock Lock(m_CSPacket);
	cItems Slots;
	a_Window.GetSlots(*(m_Client->GetPlayer()), Slots);
	SendWindowSlots(a_Window.GetWindowID(), (int)Slots.size(), &(Slots[0]));
}





void cProtocol125::SendWindowClose(const cWindow & a_Window)
{
	if (a_Window.GetWindowType() == cWindow::wtInventory)
	{
		// Do not send inventory-window-close
		return;
	}
	
	cCSLock Lock(m_CSPacket);
	WriteByte(PACKET_WINDOW_CLOSE);
	WriteChar(a_Window.GetWindowID());
	Flush();
}





void cProtocol125::SendWindowOpen(const cWindow & a_Window)
{
	if (a_Window.GetWindowType() < 0)
	{
		// Do not send for inventory windows
		return;
	}
	cCSLock Lock(m_CSPacket);
	WriteByte  (PACKET_WINDOW_OPEN);
	WriteChar  (a_Window.GetWindowID());
	WriteByte  ((Byte)a_Window.GetWindowType());
	WriteString(a_Window.GetWindowTitle());
	WriteByte  ((Byte)a_Window.GetNumNonInventorySlots());
	Flush();
}





void cProtocol125::SendWindowProperty(const cWindow & a_Window, short a_Property, short a_Value)
{
	cCSLock Lock(m_CSPacket);
	WriteByte (PACKET_WINDOW_PROPERTY);
	WriteChar (a_Window.GetWindowID());
	WriteShort(a_Property);
	WriteShort(a_Value);
	Flush();
}





AString cProtocol125::GetAuthServerID(void)
{
	// http://wiki.vg/wiki/index.php?title=Session&oldid=2262
	// The server generates a random hash and that is used for all clients, unmodified
	return cRoot::Get()->GetServer()->GetServerID();
}





void cProtocol125::SendData(const char * a_Data, size_t a_Size)
{
	m_Client->SendData(a_Data, a_Size);
}





void cProtocol125::DataReceived(const char * a_Data, size_t a_Size)
{
	if (!m_ReceivedData.Write(a_Data, a_Size))
	{
		// Too much data in the incoming queue, report to caller:
		m_Client->PacketBufferFull();
		return;
	}

	// Parse and handle all complete packets in m_ReceivedData:
	while (m_ReceivedData.CanReadBytes(1))
	{
		unsigned char PacketType;
		m_ReceivedData.ReadByte(PacketType);
		switch (ParsePacket(PacketType))
		{
			case PARSE_UNKNOWN:
			{
				// An unknown packet has been received, notify the client and abort:
				m_Client->PacketUnknown(PacketType);
				return;
			}
			case PARSE_ERROR:
			{
				// An error occurred while parsing a known packet, notify the client and abort:
				m_Client->PacketError(PacketType);
				return;
			}
			case PARSE_INCOMPLETE:
			{
				// Incomplete packet, bail out and process with the next batch of data
				m_ReceivedData.ResetRead();
				return;
			}
			default:
			{
				// Packet successfully parsed, commit the read data and try again one more packet
				m_ReceivedData.CommitRead();
				break;
			}
		}
	}
}





int cProtocol125::ParsePacket(unsigned char a_PacketType)
{
	switch (a_PacketType)
	{
		default: return PARSE_UNKNOWN;
		case PACKET_ANIMATION:                 return ParseArmAnim();
		case PACKET_BLOCK_DIG:                 return ParseBlockDig();
		case PACKET_BLOCK_PLACE:               return ParseBlockPlace();
		case PACKET_CHAT:                      return ParseChat();
		case PACKET_CREATIVE_INVENTORY_ACTION: return ParseCreativeInventoryAction();
		case PACKET_DISCONNECT:                return ParseDisconnect();
		case PACKET_HANDSHAKE:                 return ParseHandshake();
		case PACKET_KEEP_ALIVE:                return ParseKeepAlive();
		case PACKET_LOGIN:                     return ParseLogin();
		case PACKET_PACKET_ENTITY_ACTION:      return ParseEntityAction();
		case PACKET_PING:                      return ParsePing();
		case PACKET_PLAYER_ABILITIES:          return ParsePlayerAbilities();
		case PACKET_PLAYER_LOOK:               return ParsePlayerLook();
		case PACKET_PLAYER_MOVE_LOOK:          return ParsePlayerMoveLook();
		case PACKET_PLAYER_ON_GROUND:          return ParsePlayerOnGround();
		case PACKET_PLAYER_POS:                return ParsePlayerPosition();
		case PACKET_PLUGIN_MESSAGE:            return ParsePluginMessage();
		case PACKET_RESPAWN:                   return ParseRespawn();
		case PACKET_SLOT_SELECTED:             return ParseSlotSelected();
		case PACKET_UPDATE_SIGN:               return ParseUpdateSign();
		case PACKET_USE_ENTITY:                return ParseUseEntity();
		case PACKET_WINDOW_CLICK:              return ParseWindowClick();
		case PACKET_WINDOW_CLOSE:              return ParseWindowClose();
	}
}





int cProtocol125::ParseArmAnim(void)
{
	HANDLE_PACKET_READ(ReadBEInt, int,  EntityID);
	HANDLE_PACKET_READ(ReadChar,  char, Animation);
	m_Client->HandleAnimation(Animation);
	return PARSE_OK;
}





int cProtocol125::ParseBlockDig(void)
{
	HANDLE_PACKET_READ(ReadChar,	char, Status);
	HANDLE_PACKET_READ(ReadBEInt, int,  PosX);
	HANDLE_PACKET_READ(ReadByte,	Byte, PosY);
	HANDLE_PACKET_READ(ReadBEInt, int,  PosZ);
	HANDLE_PACKET_READ(ReadChar,	char, BlockFace);
	m_Client->HandleLeftClick(PosX, PosY, PosZ, static_cast<eBlockFace>(BlockFace), Status);
	return PARSE_OK;
}





int cProtocol125::ParseBlockPlace(void)
{
	HANDLE_PACKET_READ(ReadBEInt, int,  PosX);
	HANDLE_PACKET_READ(ReadByte,  Byte, PosY);
	HANDLE_PACKET_READ(ReadBEInt, int,  PosZ);
	HANDLE_PACKET_READ(ReadChar,  char, BlockFace);

	cItem HeldItem;
	int res = ParseItem(HeldItem);
	if (res < 0)
	{
		return res;
	}

	// 1.2.5 didn't have any cursor position, so use 8, 8, 8, so that halfslabs and stairs work correctly and the special value is recognizable.
	m_Client->HandleRightClick(PosX, PosY, PosZ, static_cast<eBlockFace>(BlockFace), 8, 8, 8, HeldItem);
	return PARSE_OK;
}





int cProtocol125::ParseChat(void)
{
	HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Message);
	m_Client->HandleChat(Message);
	return PARSE_OK;
}





int cProtocol125::ParseCreativeInventoryAction(void)
{
	HANDLE_PACKET_READ(ReadBEShort, short, SlotNum);
	cItem HeldItem;
	int res = ParseItem(HeldItem);
	if (res < 0)
	{
		return res;
	}
	m_Client->HandleCreativeInventory(SlotNum, HeldItem);
	return PARSE_OK;
}





int cProtocol125::ParseDisconnect(void)
{
	HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Reason);
	m_Client->HandleDisconnect(Reason);
	return PARSE_OK;
}





int cProtocol125::ParseEntityAction(void)
{
	HANDLE_PACKET_READ(ReadBEInt, int,  EntityID);
	HANDLE_PACKET_READ(ReadChar,  char, ActionID);

	switch (ActionID)
	{
		case 1: m_Client->HandleEntityCrouch(EntityID, true);     break; // Crouch
		case 2: m_Client->HandleEntityCrouch(EntityID, false);    break; // Uncrouch
		case 3: m_Client->HandleEntityLeaveBed(EntityID);         break; // Leave Bed
		case 4: m_Client->HandleEntitySprinting(EntityID, true);  break; // Start sprinting
		case 5: m_Client->HandleEntitySprinting(EntityID, false); break; // Stop sprinting
	}

	return PARSE_OK;
}





int cProtocol125::ParseHandshake(void)
{
	HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Username);

	AStringVector UserData = StringSplit(Username, ";"); // "FakeTruth;localhost:25565"
	if (UserData.empty())
	{
		m_Client->Kick("Did not receive username");
		return PARSE_OK;
	}
	m_Username = UserData[0];

	LOGD("HANDSHAKE %s", Username.c_str());

	if (!m_Client->HandleHandshake( m_Username ))
	{
		return PARSE_OK; // Player is not allowed into the server
	}

	SendHandshake(cRoot::Get()->GetServer()->GetServerID());
	LOGD("User \"%s\" was sent a handshake response", m_Username.c_str());

	return PARSE_OK;
}





int cProtocol125::ParseKeepAlive(void)
{
	HANDLE_PACKET_READ(ReadBEInt, int, KeepAliveID);
	m_Client->HandleKeepAlive(KeepAliveID);
	return PARSE_OK;
}





int cProtocol125::ParseLogin(void)
{
	HANDLE_PACKET_READ(ReadBEInt,           int,     ProtocolVersion);
	HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Username);
	HANDLE_PACKET_READ(ReadBEUTF16String16, AString, LevelType);
	HANDLE_PACKET_READ(ReadBEInt,           int,     ServerMode);
	HANDLE_PACKET_READ(ReadBEInt,           int,     Dimension);
	HANDLE_PACKET_READ(ReadChar,            char,    Difficulty);
	HANDLE_PACKET_READ(ReadByte,            Byte,    WorldHeight);
	HANDLE_PACKET_READ(ReadByte,            Byte,    MaxPlayers);

	if (ProtocolVersion < 29)
	{
		m_Client->Kick("Your client is outdated!");
		return PARSE_OK;
	}
	else if (ProtocolVersion > 29)
	{
		m_Client->Kick("Your client version is higher than the server!");
		return PARSE_OK;
	}

	if (m_Username.compare(Username) != 0)
	{
		LOGWARNING("Login Username (\"%s\") does not match Handshake username (\"%s\") for client @ \"%s\", kicking",
			Username.c_str(),
			m_Username.c_str(),
			m_Client->GetIPString().c_str()
		);
		m_Client->Kick("Hacked client");  // Don't tell them why we don't want them
		return PARSE_OK;
	}

	m_Client->HandleLogin(ProtocolVersion, Username);
	return PARSE_OK;
}





int cProtocol125::ParsePing(void)
{
	// Packet has no more data
	m_Client->HandlePing();
	return PARSE_OK;
}






int cProtocol125::ParsePlayerAbilities(void)
{
	HANDLE_PACKET_READ(ReadBool, bool, Invulnerable);
	HANDLE_PACKET_READ(ReadBool, bool, IsFlying);
	HANDLE_PACKET_READ(ReadBool, bool, CanFly);
	HANDLE_PACKET_READ(ReadBool, bool, InstaMine);
	// TODO: m_Client->HandlePlayerAbilities(...);
	return PARSE_OK;
}





int cProtocol125::ParsePlayerLook(void)
{
	HANDLE_PACKET_READ(ReadBEFloat, float, Rotation);
	HANDLE_PACKET_READ(ReadBEFloat, float, Pitch);
	HANDLE_PACKET_READ(ReadBool,    bool,  IsOnGround);
	m_Client->HandlePlayerLook(Rotation, Pitch, IsOnGround);
	return PARSE_OK;
}





int cProtocol125::ParsePlayerMoveLook(void)
{
	HANDLE_PACKET_READ(ReadBEDouble, double, PosX);
	HANDLE_PACKET_READ(ReadBEDouble, double, PosY);
	HANDLE_PACKET_READ(ReadBEDouble, double, Stance);
	HANDLE_PACKET_READ(ReadBEDouble, double, PosZ);
	HANDLE_PACKET_READ(ReadBEFloat,  float,  Rotation);
	HANDLE_PACKET_READ(ReadBEFloat,  float,  Pitch);
	HANDLE_PACKET_READ(ReadBool,     bool,   IsOnGround);
	// LOGD("Recv PML: {%0.2f, %0.2f, %0.2f}, Stance %0.2f, Gnd: %d", PosX, PosY, PosZ, Stance, IsOnGround ? 1 : 0);
	m_Client->HandlePlayerMoveLook(PosX, PosY, PosZ, Stance, Rotation, Pitch, IsOnGround);
	return PARSE_OK;
}





int cProtocol125::ParsePlayerOnGround(void)
{
	HANDLE_PACKET_READ(ReadBool, bool, IsOnGround);
	// TODO: m_Client->HandleFlying(IsOnGround);
	return PARSE_OK;
}





int cProtocol125::ParsePlayerPosition(void)
{
	HANDLE_PACKET_READ(ReadBEDouble, double, PosX);
	HANDLE_PACKET_READ(ReadBEDouble, double, PosY);
	HANDLE_PACKET_READ(ReadBEDouble, double, Stance);
	HANDLE_PACKET_READ(ReadBEDouble, double, PosZ);
	HANDLE_PACKET_READ(ReadBool,     bool,   IsOnGround);
	m_Client->HandlePlayerPos(PosX, PosY, PosZ, Stance, IsOnGround);
	return PARSE_OK;
}





int cProtocol125::ParsePluginMessage(void)
{
	HANDLE_PACKET_READ(ReadBEUTF16String16, AString, ChannelName);
	HANDLE_PACKET_READ(ReadBEShort,         short,   Length);
	AString Data;
	if (!m_ReceivedData.ReadString(Data, (size_t)Length))
	{
		m_ReceivedData.CheckValid();
		return PARSE_INCOMPLETE;
	}
	m_ReceivedData.CheckValid();
	
	// TODO: Process the data
	LOGD("Received %d bytes of plugin data on channel \"%s\".", Length, ChannelName.c_str());
	
	return PARSE_OK;
}





int cProtocol125::ParseRespawn(void)
{
	HANDLE_PACKET_READ(ReadBEInt,           int,     Dimension);
	HANDLE_PACKET_READ(ReadChar,            char,    Difficulty);
	HANDLE_PACKET_READ(ReadChar,            char,    CreativeMode);
	HANDLE_PACKET_READ(ReadBEShort,         short,   WorldHeight);
	HANDLE_PACKET_READ(ReadBEUTF16String16, AString, LevelType);
	m_Client->HandleRespawn();
	return PARSE_OK;
}





int cProtocol125::ParseSlotSelected(void)
{
	HANDLE_PACKET_READ(ReadBEShort, short, SlotNum);
	m_Client->HandleSlotSelected(SlotNum);
	return PARSE_OK;
}





int cProtocol125::ParseUpdateSign(void)
{
	HANDLE_PACKET_READ(ReadBEInt,           int,     BlockX);
	HANDLE_PACKET_READ(ReadBEShort,         short,   BlockY);
	HANDLE_PACKET_READ(ReadBEInt,           int,     BlockZ);
	HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Line1);
	HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Line2);
	HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Line3);
	HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Line4);
	m_Client->HandleUpdateSign(BlockX, BlockY, BlockZ, Line1, Line2, Line3, Line4);
	return PARSE_OK;
}





int cProtocol125::ParseUseEntity(void)
{
	HANDLE_PACKET_READ(ReadBEInt, int,  SourceEntityID);
	HANDLE_PACKET_READ(ReadBEInt, int,  TargetEntityID);
	HANDLE_PACKET_READ(ReadBool,  bool, IsLeftClick);
	m_Client->HandleUseEntity(TargetEntityID, IsLeftClick);
	return PARSE_OK;
}





int cProtocol125::ParseWindowClick(void)
{
	HANDLE_PACKET_READ(ReadChar,    char,  WindowID);
	HANDLE_PACKET_READ(ReadBEShort, short, SlotNum);
	HANDLE_PACKET_READ(ReadBool,    bool,  IsRightClick);
	HANDLE_PACKET_READ(ReadBEShort, short, TransactionID);
	HANDLE_PACKET_READ(ReadBool,    bool,  IsShiftPressed);
	cItem HeldItem;
	int res = ParseItem(HeldItem);
	if (res < 0)
	{
		return res;
	}
	
	// Convert IsShiftPressed, IsRightClick, SlotNum and HeldItem into eClickAction used in the newer protocols:
	eClickAction Action;
	if (IsRightClick)
	{
		if (IsShiftPressed)
		{
			Action = caShiftRightClick;
		}
		else
		{
			if (SlotNum == -999)
			{
				Action = (HeldItem.IsEmpty()) ? caRightClickOutsideHoldNothing : caRightClickOutside;
			}
			else
			{
				Action = caRightClick;
			}
		}
	}
	else
	{
		// IsLeftClick
		if (IsShiftPressed)
		{
			Action = caShiftLeftClick;
		}
		else
		{
			if (SlotNum == -999)
			{
				Action = (HeldItem.IsEmpty()) ? caLeftClickOutsideHoldNothing : caRightClickOutside;
			}
			else
			{
				Action = caLeftClick;
			}
		}
	}
	m_Client->HandleWindowClick(WindowID, SlotNum, Action, HeldItem);
	return PARSE_OK;
}





int cProtocol125::ParseWindowClose(void)
{
	HANDLE_PACKET_READ(ReadChar, char, WindowID);
	m_Client->HandleWindowClose(WindowID);
	return PARSE_OK;
}





void cProtocol125::SendPreChunk(int a_ChunkX, int a_ChunkZ, bool a_ShouldLoad)
{
	WriteByte(PACKET_PRE_CHUNK);
	WriteInt (a_ChunkX);
	WriteInt (a_ChunkZ);
	WriteBool(a_ShouldLoad);
	Flush();
}





void cProtocol125::SendWindowSlots(char a_WindowID, int a_NumItems, const cItem * a_Items)
{
	WriteByte (PACKET_INVENTORY_WHOLE);
	WriteChar (a_WindowID);
	WriteShort((short)a_NumItems);

	for (int j = 0; j < a_NumItems; j++)
	{
		WriteItem(a_Items[j]);
	}
	Flush();
}





void cProtocol125::WriteItem(const cItem & a_Item)
{
	short ItemType = a_Item.m_ItemType;
	ASSERT(ItemType >= -1);  // Check validity of packets in debug runtime
	if (ItemType <= 0)
	{
		// Fix, to make sure no invalid values are sent.
		ItemType = -1;
	}
	
	WriteShort(ItemType);
	if (a_Item.IsEmpty())
	{
		return;
	}
	
	WriteChar (a_Item.m_ItemCount);
	WriteShort(a_Item.m_ItemDamage);
	
	if (cItem::IsEnchantable(a_Item.m_ItemType))
	{
		// TODO: Implement enchantments
		WriteShort(-1);
	}
}





int cProtocol125::ParseItem(cItem & a_Item)
{
	HANDLE_PACKET_READ(ReadBEShort, short, ItemType);

	if (ItemType <= -1)
	{
		a_Item.Empty();
		return PARSE_OK;
	}
	a_Item.m_ItemType = ItemType;

	HANDLE_PACKET_READ(ReadChar,    char,  ItemCount);
	HANDLE_PACKET_READ(ReadBEShort, short, ItemDamage);
	a_Item.m_ItemCount  = ItemCount;
	a_Item.m_ItemDamage = ItemDamage;
	if (ItemCount <= 0)
	{
		a_Item.Empty();
	}

	if (!cItem::IsEnchantable(ItemType))
	{
		return PARSE_OK;
	}
	
	HANDLE_PACKET_READ(ReadBEShort, short, EnchantNumBytes);
	
	if (EnchantNumBytes <= 0)
	{
		return PARSE_OK;
	}
		
	// TODO: Enchantment not implemented yet!
	if (!m_ReceivedData.SkipRead((size_t)EnchantNumBytes))
	{
		return PARSE_INCOMPLETE;
	}
	
	return PARSE_OK;
}





void cProtocol125::WriteCommonMetadata(const cEntity & a_Entity)
{
	Byte CommonMetadata = 0;

	if (a_Entity.IsOnFire())
	{
		CommonMetadata |= 0x1;
	}
	if (a_Entity.IsCrouched())
	{
		CommonMetadata |= 0x2;
	}
	if (a_Entity.IsRiding())
	{
		CommonMetadata |= 0x4;
	}
	if (a_Entity.IsSprinting())
	{
		CommonMetadata |= 0x8;
	}
	if (a_Entity.IsRclking())
	{
		CommonMetadata |= 0x10;
	}
	if (a_Entity.IsInvisible())
	{
		CommonMetadata |= 0x20;
	}

	WriteByte(0x0);
	WriteByte(CommonMetadata);
}





void cProtocol125::WriteEntityMetadata(const cEntity & a_Entity)
{
	if (a_Entity.IsMinecart())
	{
		WriteByte(0x51);
		// No idea how Mojang makes their carts shakey shakey, so here is a complicated one-liner expression that does something similar
		WriteInt( (((a_Entity.GetMaxHealth() / 2) - (a_Entity.GetHealth() - (a_Entity.GetMaxHealth() / 2))) * ((const cMinecart &)a_Entity).LastDamage()) * 4 );
		WriteByte(0x52);
		WriteInt(1); // Shaking direction, doesn't seem to affect anything
		WriteByte(0x73);
		WriteFloat((float)(((const cMinecart &)a_Entity).LastDamage() + 10)); // Damage taken / shake effect multiplyer
		
		if (((cMinecart &)a_Entity).GetPayload() == cMinecart::mpFurnace)
		{
			WriteByte(0x10);
			WriteByte(((const cMinecartWithFurnace &)a_Entity).IsFueled() ? 1 : 0); // Fueled?
		}
	}
	else if ((a_Entity.IsProjectile() && ((cProjectileEntity &)a_Entity).GetProjectileKind() == cProjectileEntity::pkArrow))
	{
		WriteByte(0x10);
		WriteByte(((const cArrowEntity &)a_Entity).IsCritical() ? 1 : 0); // Critical hitting arrow?
	}
}





void cProtocol125::WriteMobMetadata(const cMonster & a_Mob)
{
	switch (a_Mob.GetMobType())
	{
		case cMonster::mtCreeper:
		{
			WriteByte(0x10);
			WriteChar(((const cCreeper &)a_Mob).IsBlowing() ? 1 : -1); // Blowing up?
			WriteByte(0x11);
			WriteByte(((const cCreeper &)a_Mob).IsCharged() ? 1 : 0); // Lightning-charged?
			break;
		}
		case cMonster::mtBat:
		{
			WriteByte(0x10);
			WriteByte(((const cBat &)a_Mob).IsHanging() ? 1 : 0); // Upside down?
			break;
		}
		case cMonster::mtPig:
		{
			WriteByte(0x10);
			WriteByte(((const cPig &)a_Mob).IsSaddled() ? 1 : 0); // Saddled?
			break;
		}
		case cMonster::mtVillager:
		{
			WriteByte(0x50);
			WriteInt(((const cVillager &)a_Mob).GetVilType()); // What sort of TESTIFICATE?
			break;
		}
		case cMonster::mtZombie:
		{
			WriteByte(0xC);
			WriteByte(((const cZombie &)a_Mob).IsBaby() ? 1 : 0); // Babby zombie?
			WriteByte(0xD);
			WriteByte(((const cZombie &)a_Mob).IsVillagerZombie() ? 1 : 0); // Converted zombie?
			WriteByte(0xE);
			WriteByte(((const cZombie &)a_Mob).IsConverting() ? 1 : 0); // Converted-but-converting-back zombllager?
			break;
		}
		case cMonster::mtGhast:
		{
			WriteByte(0x10);
			WriteByte(((const cGhast &)a_Mob).IsCharging()); // About to eject un flamé-bol? :P
			break;
		}
		case cMonster::mtWolf:
		{
			Byte WolfStatus = 0;
			if (((const cWolf &)a_Mob).IsSitting())
			{
				WolfStatus |= 0x1;
			}
			if (((const cWolf &)a_Mob).IsAngry())
			{
				WolfStatus |= 0x2;
			}
			if (((const cWolf &)a_Mob).IsTame())
			{
				WolfStatus |= 0x4;
			}
			WriteByte(0x10);
			WriteByte(WolfStatus);

			WriteByte(0x72);
			WriteFloat((float)(a_Mob.GetHealth())); // Tail health-o-meter (only shown when tamed, by the way)
			WriteByte(0x13);
			WriteByte(((const cWolf &)a_Mob).IsBegging() ? 1 : 0); // Ultra cute mode?
			break;
		}
		case cMonster::mtSheep:
		{
			// [1](1111)
			// [] = Is sheared? () = Color, from 0 to 15

			WriteByte(0x10);
			Byte SheepMetadata = 0;
			SheepMetadata = (Byte)((const cSheep &)a_Mob).GetFurColor();

			if (((const cSheep &)a_Mob).IsSheared())
			{
				SheepMetadata |= 0x16;
			}
			WriteByte(SheepMetadata);
			break;
		}
		case cMonster::mtEnderman:
		{
			WriteByte(0x10);
			WriteByte((Byte)(((const cEnderman &)a_Mob).GetCarriedBlock())); // Block that he stole from your house
			WriteByte(0x11);
			WriteByte((Byte)(((const cEnderman &)a_Mob).GetCarriedMeta())); // Meta of block that he stole from your house
			WriteByte(0x12);
			WriteByte(((const cEnderman &)a_Mob).IsScreaming() ? 1 : 0); // Screaming at your face?
			break;
		}
		case cMonster::mtSkeleton:
		{
			WriteByte(0xD);
			WriteByte(((const cSkeleton &)a_Mob).IsWither() ? 1 : 0); // It's a skeleton, but it's not
			break;
		}
		case cMonster::mtWitch:
		{
			WriteByte(0x15);
			WriteByte(((const cWitch &)a_Mob).IsAngry() ? 1 : 0); // Aggravated? Doesn't seem to do anything
			break;
		}
		case cMonster::mtWither:
		{
			WriteByte(0x54); // Int at index 20
			WriteInt((Int32)((const cWither &)a_Mob).GetNumInvulnerableTicks());
			WriteByte(0x66); // Float at index 6
			WriteFloat((float)(a_Mob.GetHealth()));
			break;
		}
		case cMonster::mtSlime:
		case cMonster::mtMagmaCube:
		{
			WriteByte(0x10);
			if (a_Mob.GetMobType() == cMonster::mtSlime)
			{
				WriteByte((Byte)((const cSlime &)a_Mob).GetSize()); // Size of slime - HEWGE, meh, cute BABBY SLIME
			}
			else
			{
				WriteByte((Byte)((const cMagmaCube &)a_Mob).GetSize()); // Size of slime - HEWGE, meh, cute BABBY SLIME
			}
			break;
		}
		case cMonster::mtHorse:
		{
			int Flags = 0;
			if (((const cHorse &)a_Mob).IsTame())
			{
				Flags |= 0x2;
			}
			if (((const cHorse &)a_Mob).IsSaddled())
			{
				Flags |= 0x4;
			}
			if (((const cHorse &)a_Mob).IsChested())
			{
				Flags |= 0x8;
			}
			if (((const cHorse &)a_Mob).IsBaby())
			{
				Flags |= 0x10; // IsBred flag, according to wiki.vg - don't think it does anything in multiplayer
			}
			if (((const cHorse &)a_Mob).IsEating())
			{
				Flags |= 0x20;
			}
			if (((const cHorse &)a_Mob).IsRearing())
			{
				Flags |= 0x40;
			}
			if (((const cHorse &)a_Mob).IsMthOpen())
			{
				Flags |= 0x80;
			}
			WriteByte(0x50);
			WriteInt(Flags);

			WriteByte(0x13);
			WriteByte((Byte)((const cHorse &)a_Mob).GetHorseType()); // Type of horse (donkey, chestnut, etc.)

			WriteByte(0x54);
			int Appearance = 0;
			Appearance = ((const cHorse &)a_Mob).GetHorseColor(); // Mask FF
			Appearance |= ((const cHorse &)a_Mob).GetHorseStyle() * 256; // Mask FF00, so multiply by 256
			WriteInt(Appearance);	

			WriteByte(0x56);
			WriteInt(((const cHorse &)a_Mob).GetHorseArmour()); // Horshey armour
			break;
		}
		default:
		{
			break;
		}
	}
}




