summaryrefslogtreecommitdiffstats
path: root/src/Protocol/Protocol125.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/Protocol/Protocol125.cpp')
-rw-r--r--src/Protocol/Protocol125.cpp1902
1 files changed, 1902 insertions, 0 deletions
diff --git a/src/Protocol/Protocol125.cpp b/src/Protocol/Protocol125.cpp
new file mode 100644
index 000000000..b1dd17ea1
--- /dev/null
+++ b/src/Protocol/Protocol125.cpp
@@ -0,0 +1,1902 @@
+
+// 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"
+
+
+
+
+
+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_SPAWN_EXPERIENCE_ORB = 0x1A,
+ 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_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);
+ WriteByte (a_Byte1);
+ WriteByte (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((unsigned short)a_Changes.size());
+ WriteUInt (sizeof(int) * a_Changes.size());
+ for (sSetBlockVector::const_iterator itr = a_Changes.begin(), end = a_Changes.end(); itr != end; ++itr)
+ {
+ unsigned int Coords = itr->y | (itr->z << 8) | (itr->x << 12);
+ unsigned int Blocks = itr->BlockMeta | (itr->BlockType << 4);
+ 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::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::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());
+ WriteByte((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());
+ WriteByte((char)((a_Entity.GetRotation() / 360.f) * 256));
+ WriteByte((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());
+ WriteByte(a_RelX);
+ WriteByte(a_RelY);
+ WriteByte(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());
+ WriteByte(a_RelX);
+ WriteByte(a_RelY);
+ WriteByte(a_RelZ);
+ WriteByte((char)((a_Entity.GetRotation() / 360.f) * 256));
+ WriteByte((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());
+ WriteByte(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 (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);
+ WriteByte((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(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);
+ WriteByte (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::SendPickupSpawn(const cPickup & a_Pickup)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_PICKUP_SPAWN);
+ WriteInt (a_Pickup.GetUniqueID());
+ WriteShort (a_Pickup.GetItem().m_ItemType);
+ WriteByte (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::SendPlayerAnimation(const cPlayer & a_Player, char a_Animation)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_ANIMATION);
+ WriteInt (a_Player.GetUniqueID());
+ WriteByte(a_Animation);
+ Flush();
+}
+
+
+
+
+
+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->GetRotation()));
+ 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));
+ WriteByte ((char)((a_Player.GetRot().x / 360.f) * 256));
+ WriteByte ((char)((a_Player.GetRot().y / 360.f) * 256));
+ WriteShort (HeldItem.IsEmpty() ? 0 : HeldItem.m_ItemType);
+ 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
+ WriteByte ((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(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 (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());
+ WriteByte(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());
+ WriteByte (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.GetRotation() / 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)));
+ WriteByte ((char)((a_Entity.GetRotation() / 360.f) * 256));
+ WriteByte ((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(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(), 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);
+ WriteByte(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);
+ WriteByte (a_Window.GetWindowID());
+ WriteByte (a_Window.GetWindowType());
+ WriteString(a_Window.GetWindowTitle());
+ WriteByte (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);
+ WriteByte (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, int a_Size)
+{
+ m_Client->SendData(a_Data, a_Size);
+}
+
+
+
+
+
+void cProtocol125::DataReceived(const char * a_Data, int 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();
+ }
+}
+
+
+
+
+
+#define HANDLE_PACKET_PARSE(Packet) \
+ { \
+ int res = Packet.Parse(m_ReceivedData); \
+ if (res < 0) \
+ { \
+ return res; \
+ } \
+ }
+
+
+
+
+
+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, 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, 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);
+ m_Client->HandleEntityAction(EntityID, ActionID);
+ 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, 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);
+ WriteByte (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;
+ }
+
+ WriteByte (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(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);
+ WriteByte(((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 = ((const cSheep &)a_Mob).GetFurColor(); // Fur colour
+
+ if (((const cSheep &)a_Mob).IsSheared()) // Is sheared?
+ {
+ 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::mtSlime:
+ case cMonster::mtMagmaCube:
+ {
+ WriteByte(0x10);
+ if (a_Mob.GetMobType() == cMonster::mtSlime)
+ {
+ WriteByte(((const cSlime &)a_Mob).GetSize()); // Size of slime - HEWGE, meh, cute BABBY SLIME
+ }
+ else
+ {
+ WriteByte(((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(((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;
+ }
+ }
+}
+
+
+
+