diff options
Diffstat (limited to 'src/Protocol/Protocol125.cpp')
-rw-r--r-- | src/Protocol/Protocol125.cpp | 1884 |
1 files changed, 1884 insertions, 0 deletions
diff --git a/src/Protocol/Protocol125.cpp b/src/Protocol/Protocol125.cpp new file mode 100644 index 000000000..54be65b12 --- /dev/null +++ b/src/Protocol/Protocol125.cpp @@ -0,0 +1,1884 @@ + +// 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 "../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_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::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; + } + } +} + + + + |