2012-08-27 13:31:16 -04:00
|
|
|
|
|
|
|
// Protocol125.cpp
|
|
|
|
|
|
|
|
// Implements the cProtocol125 class representing the release 1.2.5 protocol (#29)
|
|
|
|
|
|
|
|
#include "Globals.h"
|
|
|
|
|
|
|
|
#include "Protocol125.h"
|
|
|
|
|
|
|
|
#include "cClientHandle.h"
|
|
|
|
#include "ChunkDataSerializer.h"
|
|
|
|
#include "cEntity.h"
|
|
|
|
#include "cMonster.h"
|
|
|
|
#include "cPickup.h"
|
|
|
|
#include "cPlayer.h"
|
2012-08-28 17:59:49 -04:00
|
|
|
#include "cChatColor.h"
|
|
|
|
#include "cWindow.h"
|
2012-08-30 04:19:19 -04:00
|
|
|
#include "cRoot.h"
|
|
|
|
#include "cServer.h"
|
2012-08-28 17:59:49 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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,
|
2012-08-29 17:02:39 -04:00
|
|
|
PACKET_PLAYER_ON_GROUND = 0x0a,
|
|
|
|
PACKET_PLAYER_POS = 0x0b,
|
|
|
|
PACKET_PLAYER_LOOK = 0x0c,
|
|
|
|
PACKET_PLAYER_MOVE_LOOK = 0x0d,
|
2012-08-28 17:59:49 -04:00
|
|
|
PACKET_BLOCK_DIG = 0x0e,
|
|
|
|
PACKET_BLOCK_PLACE = 0x0f,
|
|
|
|
PACKET_SLOT_SELECTED = 0x10,
|
|
|
|
PACKET_ADD_TO_INV = 0x11, // TODO: Sure this is not Use Bed??
|
|
|
|
PACKET_ANIMATION = 0x12,
|
|
|
|
PACKET_PACKET_ENTITY_ACTION = 0x13,
|
|
|
|
PACKET_PLAYER_SPAWN = 0x14,
|
|
|
|
PACKET_PICKUP_SPAWN = 0x15,
|
|
|
|
PACKET_COLLECT_PICKUP = 0x16,
|
|
|
|
PACKET_ADD_VEHICLE = 0x17,
|
|
|
|
PACKET_SPAWN_MOB = 0x18,
|
|
|
|
PACKET_DESTROY_ENTITY = 0x1d,
|
|
|
|
PACKET_ENTITY = 0x1e,
|
2012-08-29 17:02:39 -04:00
|
|
|
PACKET_ENT_REL_MOVE = 0x1f,
|
2012-08-28 17:59:49 -04:00
|
|
|
PACKET_ENT_LOOK = 0x20,
|
2012-08-29 17:02:39 -04:00
|
|
|
PACKET_ENT_REL_MOVE_LOOK = 0x21,
|
2012-08-28 17:59:49 -04:00
|
|
|
PACKET_ENT_TELEPORT = 0x22,
|
|
|
|
PACKET_ENT_HEAD_LOOK = 0x23,
|
|
|
|
PACKET_ENT_STATUS = 0x26,
|
|
|
|
PACKET_METADATA = 0x28,
|
|
|
|
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 = 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_INVENTORY_PROGRESS = 0x69,
|
|
|
|
PACKET_CREATIVE_INVENTORY_ACTION = 0x6B,
|
|
|
|
PACKET_UPDATE_SIGN = 0x82,
|
|
|
|
PACKET_PLAYER_LIST_ITEM = 0xC9,
|
|
|
|
PACKET_PLAYER_ABILITIES = 0xca,
|
|
|
|
PACKET_PING = 0xfe,
|
|
|
|
PACKET_DISCONNECT = 0xff
|
|
|
|
} ;
|
2012-08-27 13:31:16 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-29 17:02:39 -04:00
|
|
|
#define HANDLE_PACKET_READ(Proc, Type, Var) \
|
|
|
|
Type Var; \
|
|
|
|
{ \
|
|
|
|
if (!m_ReceivedData.Proc(Var)) \
|
|
|
|
{ \
|
|
|
|
return PARSE_INCOMPLETE; \
|
|
|
|
} \
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef unsigned char Byte;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-27 13:31:16 -04:00
|
|
|
cProtocol125::cProtocol125(cClientHandle * a_Client) :
|
|
|
|
super(a_Client),
|
|
|
|
m_ReceivedData(64 KiB)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-31 17:59:57 -04:00
|
|
|
void cProtocol125::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType)
|
2012-08-27 13:31:16 -04:00
|
|
|
{
|
2012-08-31 17:59:57 -04:00
|
|
|
UNUSED(a_BlockType);
|
|
|
|
|
2012-08-27 13:31:16 -04:00
|
|
|
cCSLock Lock(m_CSPacket);
|
2012-08-28 17:59:49 -04:00
|
|
|
WriteByte (PACKET_BLOCK_ACTION);
|
|
|
|
WriteInt (a_BlockX);
|
|
|
|
WriteShort((short)a_BlockY);
|
|
|
|
WriteInt (a_BlockZ);
|
|
|
|
WriteByte (a_Byte1);
|
|
|
|
WriteByte (a_Byte2);
|
|
|
|
Flush();
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol125::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
|
|
|
|
{
|
|
|
|
cCSLock Lock(m_CSPacket);
|
2012-08-28 17:59:49 -04:00
|
|
|
WriteByte(PACKET_BLOCK_CHANGE);
|
|
|
|
WriteInt (a_BlockX);
|
2012-08-30 05:56:59 -04:00
|
|
|
WriteByte((unsigned char)a_BlockY);
|
2012-08-28 17:59:49 -04:00
|
|
|
WriteInt (a_BlockZ);
|
|
|
|
WriteByte(a_BlockType);
|
|
|
|
WriteByte(a_BlockMeta);
|
|
|
|
Flush();
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2012-08-28 17:59:49 -04:00
|
|
|
WriteByte (PACKET_MULTI_BLOCK);
|
|
|
|
WriteInt (a_ChunkX);
|
|
|
|
WriteInt (a_ChunkZ);
|
|
|
|
WriteShort((unsigned short)a_Changes.size());
|
|
|
|
WriteInt (sizeof(int) * a_Changes.size());
|
|
|
|
for (sSetBlockVector::const_iterator itr = a_Changes.begin(), end = a_Changes.end(); itr != end; ++itr)
|
2012-08-27 13:31:16 -04:00
|
|
|
{
|
|
|
|
unsigned int Coords = itr->y | (itr->z << 8) | (itr->x << 12);
|
|
|
|
unsigned int Blocks = itr->BlockMeta | (itr->BlockType << 4);
|
2012-08-28 17:59:49 -04:00
|
|
|
WriteInt(Coords << 16 | Blocks);
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
2012-08-28 17:59:49 -04:00
|
|
|
Flush();
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol125::SendChat(const AString & a_Message)
|
|
|
|
{
|
|
|
|
cCSLock Lock(m_CSPacket);
|
2012-08-28 17:59:49 -04:00
|
|
|
WriteByte (PACKET_CHAT);
|
|
|
|
WriteString(a_Message);
|
|
|
|
Flush();
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol125::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer)
|
|
|
|
{
|
|
|
|
cCSLock Lock(m_CSPacket);
|
|
|
|
|
|
|
|
// Send the pre-chunk:
|
2012-08-28 17:59:49 -04:00
|
|
|
SendPreChunk(a_ChunkX, a_ChunkZ, true);
|
2012-08-27 13:31:16 -04:00
|
|
|
|
2012-08-28 17:59:49 -04:00
|
|
|
// 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();
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol125::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player)
|
|
|
|
{
|
|
|
|
cCSLock Lock(m_CSPacket);
|
2012-08-28 17:59:49 -04:00
|
|
|
WriteByte(PACKET_COLLECT_PICKUP);
|
|
|
|
WriteInt (a_Pickup.GetUniqueID());
|
|
|
|
WriteInt (a_Player.GetUniqueID());
|
|
|
|
Flush();
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol125::SendDestroyEntity(const cEntity & a_Entity)
|
|
|
|
{
|
|
|
|
cCSLock Lock(m_CSPacket);
|
2012-08-28 17:59:49 -04:00
|
|
|
WriteByte(PACKET_DESTROY_ENTITY);
|
|
|
|
WriteInt (a_Entity.GetUniqueID());
|
|
|
|
Flush();
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol125::SendDisconnect(const AString & a_Reason)
|
|
|
|
{
|
|
|
|
cCSLock Lock(m_CSPacket);
|
2012-08-28 17:59:49 -04:00
|
|
|
WriteByte ((unsigned char)PACKET_DISCONNECT);
|
|
|
|
WriteString(a_Reason);
|
|
|
|
Flush();
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol125::SendEntHeadLook(const cEntity & a_Entity)
|
|
|
|
{
|
|
|
|
ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self
|
|
|
|
|
|
|
|
cCSLock Lock(m_CSPacket);
|
2012-08-28 17:59:49 -04:00
|
|
|
WriteByte(PACKET_ENT_HEAD_LOOK);
|
|
|
|
WriteInt (a_Entity.GetUniqueID());
|
|
|
|
WriteByte((char)((a_Entity.GetRotation() / 360.f) * 256));
|
|
|
|
Flush();
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol125::SendEntLook(const cEntity & a_Entity)
|
|
|
|
{
|
|
|
|
ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self
|
|
|
|
|
|
|
|
cCSLock Lock(m_CSPacket);
|
2012-08-28 17:59:49 -04:00
|
|
|
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();
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol125::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item)
|
|
|
|
{
|
|
|
|
cCSLock Lock(m_CSPacket);
|
2012-08-28 17:59:49 -04:00
|
|
|
WriteByte (PACKET_ENTITY_EQUIPMENT);
|
|
|
|
WriteInt (a_Entity.GetUniqueID());
|
|
|
|
WriteShort(a_SlotNum);
|
|
|
|
WriteShort(a_Item.m_ItemType);
|
|
|
|
WriteShort(a_Item.m_ItemDamage);
|
|
|
|
Flush();
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol125::SendEntityStatus(const cEntity & a_Entity, char a_Status)
|
|
|
|
{
|
|
|
|
cCSLock Lock(m_CSPacket);
|
2012-08-28 17:59:49 -04:00
|
|
|
WriteByte(PACKET_ENT_STATUS);
|
|
|
|
WriteInt (a_Entity.GetUniqueID());
|
|
|
|
WriteByte(a_Status);
|
|
|
|
Flush();
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-31 17:59:57 -04:00
|
|
|
void cProtocol125::SendEntRelMove(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::SendEntRelMoveLook(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();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-27 13:31:16 -04:00
|
|
|
void cProtocol125::SendGameMode(eGameMode a_GameMode)
|
|
|
|
{
|
|
|
|
cCSLock Lock(m_CSPacket);
|
2012-08-28 17:59:49 -04:00
|
|
|
WriteByte(PACKET_CHANGE_GAME_STATE);
|
|
|
|
WriteByte(3);
|
|
|
|
WriteByte((char)a_GameMode);
|
|
|
|
Flush();
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-28 17:59:49 -04:00
|
|
|
void cProtocol125::SendHandshake(const AString & a_ConnectionHash)
|
2012-08-27 13:31:16 -04:00
|
|
|
{
|
|
|
|
cCSLock Lock(m_CSPacket);
|
2012-08-28 17:59:49 -04:00
|
|
|
WriteByte (PACKET_HANDSHAKE);
|
|
|
|
WriteString(a_ConnectionHash);
|
|
|
|
Flush();
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol125::SendHealth(void)
|
|
|
|
{
|
|
|
|
cCSLock Lock(m_CSPacket);
|
2012-08-28 17:59:49 -04:00
|
|
|
WriteByte (PACKET_UPDATE_HEALTH);
|
|
|
|
WriteShort((short)m_Client->GetPlayer()->GetHealth());
|
|
|
|
WriteShort(m_Client->GetPlayer()->GetFoodLevel());
|
|
|
|
WriteFloat(m_Client->GetPlayer()->GetFoodSaturationLevel());
|
|
|
|
Flush();
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol125::SendInventoryProgress(char a_WindowID, short a_ProgressBar, short a_Value)
|
|
|
|
{
|
|
|
|
cCSLock Lock(m_CSPacket);
|
2012-08-28 17:59:49 -04:00
|
|
|
WriteByte (PACKET_INVENTORY_PROGRESS);
|
|
|
|
WriteByte (a_WindowID);
|
|
|
|
WriteShort(a_ProgressBar);
|
|
|
|
WriteShort(a_Value);
|
|
|
|
Flush();
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-30 05:56:59 -04:00
|
|
|
void cProtocol125::SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem & a_Item)
|
2012-08-27 13:31:16 -04:00
|
|
|
{
|
|
|
|
cCSLock Lock(m_CSPacket);
|
2012-08-28 17:59:49 -04:00
|
|
|
WriteByte (PACKET_INVENTORY_SLOT);
|
|
|
|
WriteByte (a_WindowID);
|
|
|
|
WriteShort(a_SlotNum);
|
|
|
|
WriteItem (a_Item);
|
|
|
|
Flush();
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol125::SendKeepAlive(int a_PingID)
|
|
|
|
{
|
|
|
|
cCSLock Lock(m_CSPacket);
|
2012-08-28 17:59:49 -04:00
|
|
|
WriteByte(PACKET_KEEP_ALIVE);
|
|
|
|
WriteInt (a_PingID);
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-09-02 08:09:58 -04:00
|
|
|
void cProtocol125::SendLogin(const cPlayer & a_Player, const cWorld & a_World)
|
2012-08-27 13:31:16 -04:00
|
|
|
{
|
2012-09-02 08:09:58 -04:00
|
|
|
UNUSED(a_World);
|
2012-08-27 13:31:16 -04:00
|
|
|
cCSLock Lock(m_CSPacket);
|
2012-08-28 17:59:49 -04:00
|
|
|
|
2012-08-29 17:02:39 -04:00
|
|
|
WriteByte (PACKET_LOGIN);
|
2012-08-28 17:59:49 -04:00
|
|
|
WriteInt (a_Player.GetUniqueID()); // EntityID of the player
|
|
|
|
WriteString(""); // Username, not used
|
2012-08-31 17:59:57 -04:00
|
|
|
WriteString("default"); // Level type
|
2012-08-28 17:59:49 -04:00
|
|
|
WriteInt ((int)a_Player.GetGameMode());
|
|
|
|
WriteInt (0); // TODO: Dimension (Nether / Overworld / End)
|
|
|
|
WriteByte (2); // TODO: Difficulty
|
|
|
|
WriteByte (0); // Unused
|
|
|
|
WriteByte (60); // Client list width or something
|
|
|
|
Flush();
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-28 17:59:49 -04:00
|
|
|
void cProtocol125::SendMetadata(const cEntity & a_Entity)
|
2012-08-27 13:31:16 -04:00
|
|
|
{
|
|
|
|
cCSLock Lock(m_CSPacket);
|
2012-08-28 17:59:49 -04:00
|
|
|
WriteByte(PACKET_METADATA);
|
|
|
|
WriteInt (a_Entity.GetUniqueID());
|
|
|
|
AString MetaData = GetEntityMetaData(a_Entity);
|
|
|
|
SendData(MetaData.data(), MetaData.size());
|
|
|
|
Flush();
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol125::SendPickupSpawn(const cPickup & a_Pickup)
|
|
|
|
{
|
|
|
|
cCSLock Lock(m_CSPacket);
|
2012-08-31 17:59:57 -04:00
|
|
|
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));
|
2012-08-28 17:59:49 -04:00
|
|
|
Flush();
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol125::SendPlayerAnimation(const cPlayer & a_Player, char a_Animation)
|
|
|
|
{
|
|
|
|
cCSLock Lock(m_CSPacket);
|
2012-08-28 17:59:49 -04:00
|
|
|
WriteByte(PACKET_ANIMATION);
|
|
|
|
WriteInt (a_Player.GetUniqueID());
|
|
|
|
WriteByte(a_Animation);
|
|
|
|
Flush();
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol125::SendPlayerListItem(const cPlayer & a_Player, bool a_IsOnline)
|
|
|
|
{
|
|
|
|
cCSLock Lock(m_CSPacket);
|
2012-08-28 17:59:49 -04:00
|
|
|
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_Player.GetClientHandle()->GetPing());
|
|
|
|
Flush();
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol125::SendPlayerMoveLook(void)
|
|
|
|
{
|
|
|
|
cCSLock Lock(m_CSPacket);
|
2012-08-28 17:59:49 -04:00
|
|
|
|
2012-08-27 13:31:16 -04:00
|
|
|
/*
|
|
|
|
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
|
|
|
|
);
|
|
|
|
*/
|
2012-08-28 17:59:49 -04:00
|
|
|
|
2012-08-29 17:02:39 -04:00
|
|
|
WriteByte (PACKET_PLAYER_MOVE_LOOK);
|
2012-08-28 17:59:49 -04:00
|
|
|
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 (Player->GetRotation());
|
|
|
|
WriteFloat (Player->GetPitch());
|
|
|
|
WriteBool (Player->IsOnGround());
|
|
|
|
Flush();
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol125::SendPlayerPosition(void)
|
|
|
|
{
|
|
|
|
cCSLock Lock(m_CSPacket);
|
2012-08-28 17:59:49 -04:00
|
|
|
LOGD("Ignore send PlayerPos"); // PlayerPos is a C->S packet only now
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol125::SendPlayerSpawn(const cPlayer & a_Player)
|
|
|
|
{
|
2012-08-28 17:59:49 -04:00
|
|
|
const cItem & HeldItem = a_Player.GetEquippedItem();
|
2012-08-27 13:31:16 -04:00
|
|
|
cCSLock Lock(m_CSPacket);
|
2012-08-31 17:59:57 -04:00
|
|
|
WriteByte (PACKET_PLAYER_SPAWN);
|
2012-08-28 17:59:49 -04:00
|
|
|
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();
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol125::SendRespawn(void)
|
|
|
|
{
|
|
|
|
cCSLock Lock(m_CSPacket);
|
2012-08-28 17:59:49 -04:00
|
|
|
WriteByte (PACKET_RESPAWN);
|
|
|
|
WriteInt (0); // TODO: Dimension; 0 = Overworld
|
|
|
|
WriteByte (2); // TODO: Difficulty; 2 = Normal
|
|
|
|
WriteByte ((char)m_Client->GetPlayer()->GetGameMode());
|
|
|
|
WriteShort (256); // Current world height
|
2012-08-31 17:59:57 -04:00
|
|
|
WriteString("default");
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol125::SendSpawnMob(const cMonster & a_Mob)
|
|
|
|
{
|
|
|
|
cCSLock Lock(m_CSPacket);
|
2012-08-31 17:59:57 -04:00
|
|
|
WriteByte (PACKET_SPAWN_MOB);
|
|
|
|
WriteInt (a_Mob.GetUniqueID());
|
|
|
|
WriteByte (a_Mob.GetMobType());
|
|
|
|
WriteVectorI((Vector3i)(a_Mob.GetPosition() * 32));
|
|
|
|
WriteByte (0);
|
|
|
|
WriteByte (0);
|
|
|
|
WriteByte (0);
|
2012-08-28 17:59:49 -04:00
|
|
|
AString MetaData = GetEntityMetaData(a_Mob);
|
|
|
|
SendData (MetaData.data(), MetaData.size());
|
|
|
|
Flush();
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol125::SendTeleportEntity(const cEntity & a_Entity)
|
|
|
|
{
|
|
|
|
cCSLock Lock(m_CSPacket);
|
2012-08-28 17:59:49 -04:00
|
|
|
WriteByte (PACKET_ENT_TELEPORT);
|
|
|
|
WriteInt (a_Entity.GetUniqueID());
|
|
|
|
WriteVectorI((Vector3i)(a_Entity.GetPosition() * 32));
|
|
|
|
WriteByte ((char)((a_Entity.GetRotation() / 360.f) * 256));
|
|
|
|
WriteByte ((char)((a_Entity.GetPitch() / 360.f) * 256));
|
|
|
|
Flush();
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol125::SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ)
|
|
|
|
{
|
|
|
|
cCSLock Lock(m_CSPacket);
|
2012-08-28 17:59:49 -04:00
|
|
|
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();
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol125::SendTimeUpdate(Int64 a_WorldTime)
|
|
|
|
{
|
|
|
|
cCSLock Lock(m_CSPacket);
|
2012-08-28 17:59:49 -04:00
|
|
|
WriteByte (PACKET_UPDATE_TIME);
|
|
|
|
WriteInt64(a_WorldTime);
|
|
|
|
Flush();
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol125::SendUnloadChunk(int a_ChunkX, int a_ChunkZ)
|
|
|
|
{
|
|
|
|
cCSLock Lock(m_CSPacket);
|
2012-08-28 17:59:49 -04:00
|
|
|
SendPreChunk(a_ChunkX, a_ChunkZ, false);
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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);
|
2012-08-28 17:59:49 -04:00
|
|
|
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();
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol125::SendWeather(eWeather a_Weather)
|
|
|
|
{
|
|
|
|
cCSLock Lock(m_CSPacket);
|
|
|
|
switch( a_Weather )
|
|
|
|
{
|
|
|
|
case eWeather_Sunny:
|
|
|
|
{
|
2012-08-28 17:59:49 -04:00
|
|
|
WriteByte(PACKET_CHANGE_GAME_STATE);
|
|
|
|
WriteByte(2); // Stop rain
|
|
|
|
WriteByte(0); // Unused
|
|
|
|
Flush();
|
2012-08-27 13:31:16 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case eWeather_Rain:
|
|
|
|
case eWeather_ThunderStorm:
|
|
|
|
{
|
2012-08-28 17:59:49 -04:00
|
|
|
WriteByte(PACKET_CHANGE_GAME_STATE);
|
|
|
|
WriteByte(1); // Begin rain
|
|
|
|
WriteByte(0); // Unused
|
|
|
|
Flush();
|
2012-08-27 13:31:16 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol125::SendWholeInventory(const cInventory & a_Inventory)
|
|
|
|
{
|
|
|
|
cCSLock Lock(m_CSPacket);
|
2012-08-28 17:59:49 -04:00
|
|
|
SendWholeInventory(0, a_Inventory.c_NumSlots, a_Inventory.GetSlots());
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol125::SendWholeInventory(const cWindow & a_Window)
|
|
|
|
{
|
|
|
|
cCSLock Lock(m_CSPacket);
|
2012-08-28 17:59:49 -04:00
|
|
|
SendWholeInventory(
|
2012-08-30 05:56:59 -04:00
|
|
|
(char)a_Window.GetWindowID(),
|
2012-08-28 17:59:49 -04:00
|
|
|
a_Window.GetNumSlots(),
|
|
|
|
a_Window.GetSlots()
|
|
|
|
);
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol125::SendWindowClose(char a_WindowID)
|
|
|
|
{
|
|
|
|
cCSLock Lock(m_CSPacket);
|
2012-08-28 17:59:49 -04:00
|
|
|
WriteByte(PACKET_WINDOW_CLOSE);
|
|
|
|
WriteByte(a_WindowID);
|
|
|
|
Flush();
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol125::SendWindowOpen(char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots)
|
|
|
|
{
|
2012-08-31 17:59:57 -04:00
|
|
|
if (a_WindowType < 0)
|
|
|
|
{
|
|
|
|
// Do not send for inventory windows
|
|
|
|
return;
|
|
|
|
}
|
2012-08-27 13:31:16 -04:00
|
|
|
cCSLock Lock(m_CSPacket);
|
2012-08-28 17:59:49 -04:00
|
|
|
WriteByte (PACKET_WINDOW_OPEN);
|
|
|
|
WriteByte (a_WindowID);
|
|
|
|
WriteByte (a_WindowType);
|
|
|
|
WriteString(a_WindowTitle);
|
|
|
|
WriteByte (a_NumSlots);
|
|
|
|
Flush();
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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))
|
|
|
|
{
|
2012-08-29 17:02:39 -04:00
|
|
|
case PARSE_UNKNOWN:
|
2012-08-27 13:31:16 -04:00
|
|
|
{
|
|
|
|
// An unknown packet has been received, notify the client and abort:
|
|
|
|
m_Client->PacketUnknown(PacketType);
|
|
|
|
return;
|
|
|
|
}
|
2012-08-29 17:02:39 -04:00
|
|
|
case PARSE_ERROR:
|
2012-08-27 13:31:16 -04:00
|
|
|
{
|
|
|
|
// An error occurred while parsing a known packet, notify the client and abort:
|
|
|
|
m_Client->PacketError(PacketType);
|
|
|
|
return;
|
|
|
|
}
|
2012-08-29 17:02:39 -04:00
|
|
|
case PARSE_INCOMPLETE:
|
2012-08-27 13:31:16 -04:00
|
|
|
{
|
|
|
|
// 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)
|
|
|
|
{
|
2012-08-29 17:02:39 -04:00
|
|
|
default: return PARSE_UNKNOWN;
|
2012-08-28 17:59:49 -04:00
|
|
|
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();
|
2012-08-29 17:02:39 -04:00
|
|
|
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();
|
2012-08-28 17:59:49 -04:00
|
|
|
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();
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define HANDLE_PACKET_PARSE(Packet) \
|
|
|
|
{ \
|
|
|
|
int res = Packet.Parse(m_ReceivedData); \
|
|
|
|
if (res < 0) \
|
|
|
|
{ \
|
|
|
|
return res; \
|
|
|
|
} \
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-29 17:02:39 -04:00
|
|
|
int cProtocol125::ParseArmAnim(void)
|
2012-08-27 13:31:16 -04:00
|
|
|
{
|
2012-08-29 17:02:39 -04:00
|
|
|
HANDLE_PACKET_READ(ReadBEInt, int, EntityID);
|
|
|
|
HANDLE_PACKET_READ(ReadChar, char, Animation);
|
|
|
|
m_Client->HandleAnimation(Animation);
|
|
|
|
return PARSE_OK;
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-29 17:02:39 -04:00
|
|
|
int cProtocol125::ParseBlockDig(void)
|
2012-08-27 13:31:16 -04:00
|
|
|
{
|
2012-08-29 17:02:39 -04:00
|
|
|
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, Direction);
|
|
|
|
m_Client->HandleBlockDig(PosX, PosY, PosZ, Direction, Status);
|
|
|
|
return PARSE_OK;
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-29 17:02:39 -04:00
|
|
|
int cProtocol125::ParseBlockPlace(void)
|
2012-08-27 13:31:16 -04:00
|
|
|
{
|
2012-08-29 17:02:39 -04:00
|
|
|
HANDLE_PACKET_READ(ReadBEInt, int, PosX);
|
|
|
|
HANDLE_PACKET_READ(ReadByte, Byte, PosY);
|
|
|
|
HANDLE_PACKET_READ(ReadBEInt, int, PosZ);
|
|
|
|
HANDLE_PACKET_READ(ReadChar, char, Direction);
|
|
|
|
|
|
|
|
cItem HeldItem;
|
|
|
|
int res = ParseItem(HeldItem);
|
|
|
|
if (res < 0)
|
|
|
|
{
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_Client->HandleBlockPlace(PosX, PosY, PosZ, Direction, HeldItem);
|
|
|
|
return PARSE_OK;
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-29 17:02:39 -04:00
|
|
|
int cProtocol125::ParseChat(void)
|
2012-08-27 13:31:16 -04:00
|
|
|
{
|
2012-08-29 17:02:39 -04:00
|
|
|
HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Message);
|
|
|
|
m_Client->HandleChat(Message);
|
|
|
|
return PARSE_OK;
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-29 17:02:39 -04:00
|
|
|
int cProtocol125::ParseCreativeInventoryAction(void)
|
2012-08-27 13:31:16 -04:00
|
|
|
{
|
2012-08-29 17:02:39 -04:00
|
|
|
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;
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-29 17:02:39 -04:00
|
|
|
int cProtocol125::ParseDisconnect(void)
|
2012-08-27 13:31:16 -04:00
|
|
|
{
|
2012-08-29 17:02:39 -04:00
|
|
|
HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Reason);
|
|
|
|
m_Client->HandleDisconnect(Reason);
|
|
|
|
return PARSE_OK;
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-29 17:02:39 -04:00
|
|
|
int cProtocol125::ParseEntityAction(void)
|
2012-08-27 13:31:16 -04:00
|
|
|
{
|
2012-08-29 17:02:39 -04:00
|
|
|
HANDLE_PACKET_READ(ReadBEInt, int, EntityID);
|
|
|
|
HANDLE_PACKET_READ(ReadChar, char, ActionID);
|
|
|
|
// TODO: m_Client->HandleEntityAction(...);
|
|
|
|
return PARSE_OK;
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-29 17:02:39 -04:00
|
|
|
int cProtocol125::ParseHandshake(void)
|
2012-08-27 13:31:16 -04:00
|
|
|
{
|
2012-08-29 17:02:39 -04:00
|
|
|
HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Username);
|
2012-08-30 04:19:19 -04:00
|
|
|
|
|
|
|
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 (cRoot::Get()->GetDefaultWorld()->GetNumPlayers() >= cRoot::Get()->GetDefaultWorld()->GetMaxPlayers())
|
|
|
|
{
|
|
|
|
m_Client->Kick("The server is currently full :(-- Try again later");
|
|
|
|
return PARSE_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
SendHandshake(cRoot::Get()->GetServer()->GetServerID());
|
|
|
|
LOGD("User \"%s\" was sent a handshake response", m_Username.c_str());
|
|
|
|
|
2012-08-29 17:02:39 -04:00
|
|
|
return PARSE_OK;
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-29 17:02:39 -04:00
|
|
|
int cProtocol125::ParseKeepAlive(void)
|
2012-08-27 13:31:16 -04:00
|
|
|
{
|
2012-08-29 17:02:39 -04:00
|
|
|
HANDLE_PACKET_READ(ReadBEInt, int, KeepAliveID);
|
|
|
|
m_Client->HandleKeepAlive(KeepAliveID);
|
|
|
|
return PARSE_OK;
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-29 17:02:39 -04:00
|
|
|
int cProtocol125::ParseLogin(void)
|
2012-08-27 13:31:16 -04:00
|
|
|
{
|
2012-08-29 17:02:39 -04:00
|
|
|
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);
|
2012-08-30 04:19:19 -04:00
|
|
|
|
|
|
|
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(),
|
2012-09-04 15:05:35 -04:00
|
|
|
m_Client->GetIPString().c_str()
|
2012-08-30 04:19:19 -04:00
|
|
|
);
|
|
|
|
m_Client->Kick("Hacked client"); // Don't tell them why we don't want them
|
|
|
|
return PARSE_OK;
|
|
|
|
}
|
|
|
|
|
2012-08-29 17:02:39 -04:00
|
|
|
m_Client->HandleLogin(ProtocolVersion, Username);
|
|
|
|
return PARSE_OK;
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-29 17:02:39 -04:00
|
|
|
int cProtocol125::ParsePing(void)
|
2012-08-27 13:31:16 -04:00
|
|
|
{
|
2012-08-29 17:02:39 -04:00
|
|
|
// Packet has no more data
|
|
|
|
m_Client->HandlePing();
|
|
|
|
return PARSE_OK;
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-29 17:02:39 -04:00
|
|
|
|
|
|
|
int cProtocol125::ParsePlayerAbilities(void)
|
2012-08-27 13:31:16 -04:00
|
|
|
{
|
2012-08-29 17:02:39 -04:00
|
|
|
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;
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-29 17:02:39 -04:00
|
|
|
int cProtocol125::ParsePlayerLook(void)
|
2012-08-27 13:31:16 -04:00
|
|
|
{
|
2012-08-29 17:02:39 -04:00
|
|
|
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;
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-29 17:02:39 -04:00
|
|
|
int cProtocol125::ParsePlayerMoveLook(void)
|
2012-08-27 13:31:16 -04:00
|
|
|
{
|
2012-08-29 17:02:39 -04:00
|
|
|
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;
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-29 17:02:39 -04:00
|
|
|
int cProtocol125::ParsePlayerOnGround(void)
|
2012-08-27 13:31:16 -04:00
|
|
|
{
|
2012-08-29 17:02:39 -04:00
|
|
|
HANDLE_PACKET_READ(ReadBool, bool, IsOnGround);
|
|
|
|
// TODO: m_Client->HandleFlying(IsOnGround);
|
|
|
|
return PARSE_OK;
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-29 17:02:39 -04:00
|
|
|
int cProtocol125::ParsePlayerPosition(void)
|
2012-08-27 13:31:16 -04:00
|
|
|
{
|
2012-08-29 17:02:39 -04:00
|
|
|
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;
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-29 17:02:39 -04:00
|
|
|
int cProtocol125::ParseRespawn(void)
|
2012-08-27 13:31:16 -04:00
|
|
|
{
|
2012-08-29 17:02:39 -04:00
|
|
|
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;
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-29 17:02:39 -04:00
|
|
|
int cProtocol125::ParseSlotSelected(void)
|
2012-08-27 13:31:16 -04:00
|
|
|
{
|
2012-08-29 17:02:39 -04:00
|
|
|
HANDLE_PACKET_READ(ReadBEShort, short, SlotNum);
|
|
|
|
m_Client->HandleSlotSelected(SlotNum);
|
|
|
|
return PARSE_OK;
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-29 17:02:39 -04:00
|
|
|
int cProtocol125::ParseUpdateSign(void)
|
2012-08-27 13:31:16 -04:00
|
|
|
{
|
2012-08-29 17:02:39 -04:00
|
|
|
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;
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-29 17:02:39 -04:00
|
|
|
int cProtocol125::ParseUseEntity(void)
|
2012-08-27 13:31:16 -04:00
|
|
|
{
|
2012-08-29 17:02:39 -04:00
|
|
|
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;
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-29 17:02:39 -04:00
|
|
|
int cProtocol125::ParseWindowClick(void)
|
2012-08-27 13:31:16 -04:00
|
|
|
{
|
2012-08-29 17:02:39 -04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
m_Client->HandleWindowClick(WindowID, SlotNum, IsRightClick, IsShiftPressed, HeldItem);
|
|
|
|
return PARSE_OK;
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-29 17:02:39 -04:00
|
|
|
int cProtocol125::ParseWindowClose(void)
|
2012-08-27 13:31:16 -04:00
|
|
|
{
|
2012-08-29 17:02:39 -04:00
|
|
|
HANDLE_PACKET_READ(ReadChar, char, WindowID);
|
|
|
|
m_Client->HandleWindowClose(WindowID);
|
|
|
|
return PARSE_OK;
|
2012-08-27 13:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-28 17:59:49 -04:00
|
|
|
void cProtocol125::SendPreChunk(int a_ChunkX, int a_ChunkZ, bool a_ShouldLoad)
|
|
|
|
{
|
2012-08-29 17:02:39 -04:00
|
|
|
WriteByte(PACKET_PRE_CHUNK);
|
2012-08-28 17:59:49 -04:00
|
|
|
WriteInt (a_ChunkX);
|
|
|
|
WriteInt (a_ChunkZ);
|
|
|
|
WriteBool(a_ShouldLoad);
|
|
|
|
Flush();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol125::SendWholeInventory(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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-29 17:02:39 -04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-28 17:59:49 -04:00
|
|
|
AString cProtocol125::GetEntityMetaData(const cEntity & a_Entity)
|
|
|
|
{
|
|
|
|
// We should send all the metadata here
|
|
|
|
AString MetaData;
|
|
|
|
// Common metadata (index 0, byte):
|
|
|
|
MetaData.push_back(0);
|
|
|
|
MetaData.push_back(GetEntityMetadataFlags(a_Entity));
|
|
|
|
|
|
|
|
// TODO: Add more entity-specific metadata
|
|
|
|
|
|
|
|
MetaData.push_back(0x7f); // End metadata
|
|
|
|
return MetaData;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
char cProtocol125::GetEntityMetadataFlags(const cEntity & a_Entity)
|
|
|
|
{
|
|
|
|
char Flags = 0;
|
|
|
|
if (a_Entity.IsOnFire())
|
|
|
|
{
|
|
|
|
Flags |= 1;
|
|
|
|
}
|
|
|
|
if (a_Entity.IsCrouched())
|
|
|
|
{
|
|
|
|
Flags |= 2;
|
|
|
|
}
|
|
|
|
if (a_Entity.IsRiding())
|
|
|
|
{
|
|
|
|
Flags |= 4;
|
|
|
|
}
|
|
|
|
if (a_Entity.IsSprinting())
|
|
|
|
{
|
|
|
|
Flags |= 8;
|
|
|
|
}
|
|
|
|
if (a_Entity.IsRclking())
|
|
|
|
{
|
|
|
|
Flags |= 16;
|
|
|
|
}
|
|
|
|
return Flags;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|