1
0
cuberite-2a/source/Protocol/ProtocolRecognizer.cpp
madmaxoft@gmail.com 17a2c1b388 Adjusted the protocol framework to support different types of falling block spawning.
In brief, with cProtocol, "say what you want done, not how you want me to do it".
But still 1.4.6 crashes on falling block spawning.

git-svn-id: http://mc-server.googlecode.com/svn/trunk@1104 0a769ca7-a7f5-676a-18bf-c427514a06d6
2012-12-26 09:12:00 +00:00

687 lines
14 KiB
C++

// ProtocolRecognizer.cpp
// Implements the cProtocolRecognizer class representing the meta-protocol that recognizes possibly multiple
// protocol versions and redirects everything to them
#include "Globals.h"
#include "ProtocolRecognizer.h"
#include "Protocol125.h"
#include "Protocol132.h"
#include "Protocol14x.h"
#include "../ClientHandle.h"
#include "../Root.h"
#include "../World.h"
#include "../ChatColor.h"
cProtocolRecognizer::cProtocolRecognizer(cClientHandle * a_Client) :
super(a_Client),
m_Protocol(NULL),
m_Buffer(512)
{
}
cProtocolRecognizer::~cProtocolRecognizer()
{
delete m_Protocol;
}
AString cProtocolRecognizer::GetVersionTextFromInt(int a_ProtocolVersion)
{
switch (a_ProtocolVersion)
{
case PROTO_VERSION_1_2_5: return "1.2.5";
case PROTO_VERSION_1_3_2: return "1.3.2";
case PROTO_VERSION_1_4_2: return "1.4.2";
case PROTO_VERSION_1_4_4: return "1.4.4";
case PROTO_VERSION_1_4_6: return "1.4.6";
}
ASSERT(!"Unknown protocol version");
return Printf("Unknown protocol (%d)", a_ProtocolVersion);
}
void cProtocolRecognizer::DataReceived(const char * a_Data, int a_Size)
{
if (m_Protocol == NULL)
{
if (!m_Buffer.Write(a_Data, a_Size))
{
m_Client->Kick("Unsupported protocol version");
return;
}
if (!TryRecognizeProtocol())
{
return;
}
// The protocol has just been recognized, dump the whole m_Buffer contents into it for parsing:
AString Dump;
m_Buffer.ResetRead();
m_Buffer.ReadAll(Dump);
m_Protocol->DataReceived(Dump.data(), Dump.size());
}
else
{
m_Protocol->DataReceived(a_Data, a_Size);
}
}
void cProtocolRecognizer::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendBlockAction(a_BlockX, a_BlockY, a_BlockZ, a_Byte1, a_Byte2, a_BlockType);
}
void cProtocolRecognizer::SendBlockBreakAnim(int a_entityID, int a_BlockX, int a_BlockY, int a_BlockZ, char stage)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendBlockBreakAnim(a_entityID, a_BlockX, a_BlockY, a_BlockZ, stage);
}
void cProtocolRecognizer::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendBlockChange(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta);
}
void cProtocolRecognizer::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendBlockChanges(a_ChunkX, a_ChunkZ, a_Changes);
}
void cProtocolRecognizer::SendChat(const AString & a_Message)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendChat(a_Message);
}
void cProtocolRecognizer::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendChunkData(a_ChunkX, a_ChunkZ, a_Serializer);
}
void cProtocolRecognizer::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendCollectPickup(a_Pickup, a_Player);
}
void cProtocolRecognizer::SendDestroyEntity(const cEntity & a_Entity)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendDestroyEntity(a_Entity);
}
void cProtocolRecognizer::SendDisconnect(const AString & a_Reason)
{
if (m_Protocol != NULL)
{
m_Protocol->SendDisconnect(a_Reason);
}
else
{
// This is used when the client sends a server-ping, respond with the default packet:
WriteByte ((char)0xff); // PACKET_DISCONNECT
WriteString(a_Reason);
}
}
void cProtocolRecognizer::SendEntHeadLook(const cEntity & a_Entity)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendEntHeadLook(a_Entity);
}
void cProtocolRecognizer::SendEntLook(const cEntity & a_Entity)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendEntLook(a_Entity);
}
void cProtocolRecognizer::SendEntRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendEntRelMove(a_Entity, a_RelX, a_RelY, a_RelZ);
}
void cProtocolRecognizer::SendEntRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendEntRelMoveLook(a_Entity, a_RelX, a_RelY, a_RelZ);
}
void cProtocolRecognizer::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendEntityEquipment(a_Entity, a_SlotNum, a_Item);
}
void cProtocolRecognizer::SendEntityStatus(const cEntity & a_Entity, char a_Status)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendEntityStatus(a_Entity, a_Status);
}
void cProtocolRecognizer::SendGameMode(eGameMode a_GameMode)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendGameMode(a_GameMode);
}
void cProtocolRecognizer::SendHealth(void)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendHealth();
}
void cProtocolRecognizer::SendInventoryProgress(char a_WindowID, short a_Progressbar, short a_Value)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendInventoryProgress(a_WindowID, a_Progressbar, a_Value);
}
void cProtocolRecognizer::SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem & a_Item)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendInventorySlot(a_WindowID, a_SlotNum, a_Item);
}
void cProtocolRecognizer::SendKeepAlive(int a_PingID)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendKeepAlive(a_PingID);
}
void cProtocolRecognizer::SendLogin(const cPlayer & a_Player, const cWorld & a_World)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendLogin(a_Player, a_World);
}
void cProtocolRecognizer::SendMetadata(const cEntity & a_Entity)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendMetadata(a_Entity);
}
void cProtocolRecognizer::SendPickupSpawn(const cPickup & a_Pickup)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendPickupSpawn(a_Pickup);
}
void cProtocolRecognizer::SendPlayerAnimation(const cPlayer & a_Player, char a_Animation)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendPlayerAnimation(a_Player, a_Animation);
}
void cProtocolRecognizer::SendPlayerListItem(const cPlayer & a_Player, bool a_IsOnline)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendPlayerListItem(a_Player, a_IsOnline);
}
void cProtocolRecognizer::SendPlayerMoveLook(void)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendPlayerMoveLook();
}
void cProtocolRecognizer::SendPlayerPosition(void)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendPlayerPosition();
}
void cProtocolRecognizer::SendPlayerSpawn(const cPlayer & a_Player)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendPlayerSpawn(a_Player);
}
void cProtocolRecognizer::SendRespawn(void)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendRespawn();
}
void cProtocolRecognizer::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch);
}
void cProtocolRecognizer::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendSoundParticleEffect(a_EffectID, a_SrcX, a_SrcY, a_SrcZ, a_Data);
}
void cProtocolRecognizer::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendSpawnFallingBlock(a_FallingBlock);
}
void cProtocolRecognizer::SendSpawnMob(const cMonster & a_Mob)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendSpawnMob(a_Mob);
}
void cProtocolRecognizer::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, short a_SpeedX, short a_SpeedY, short a_SpeedZ, Byte a_Yaw, Byte a_Pitch)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendSpawnObject(a_Entity, a_ObjectType, a_ObjectData, a_SpeedX, a_SpeedY, a_SpeedZ, a_Yaw, a_Pitch);
}
void cProtocolRecognizer::SendTeleportEntity(const cEntity & a_Entity)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendTeleportEntity(a_Entity);
}
void cProtocolRecognizer::SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendThunderbolt(a_BlockX, a_BlockY, a_BlockZ);
}
void cProtocolRecognizer::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendTimeUpdate(a_WorldAge, a_TimeOfDay);
}
void cProtocolRecognizer::SendUnloadChunk(int a_ChunkX, int a_ChunkZ)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendUnloadChunk(a_ChunkX, a_ChunkZ);
}
void cProtocolRecognizer::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)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendUpdateSign(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4);
}
void cProtocolRecognizer::SendUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ )
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendUseBed(a_Entity, a_BlockX, a_BlockY, a_BlockZ);
}
void cProtocolRecognizer::SendWeather(eWeather a_Weather)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendWeather(a_Weather);
}
void cProtocolRecognizer::SendWholeInventory(const cInventory & a_Inventory)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendWholeInventory(a_Inventory);
}
void cProtocolRecognizer::SendWholeInventory(const cWindow & a_Window)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendWholeInventory(a_Window);
}
void cProtocolRecognizer::SendWindowClose(char a_WindowID)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendWindowClose(a_WindowID);
}
void cProtocolRecognizer::SendWindowOpen(char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendWindowOpen(a_WindowID, a_WindowType, a_WindowTitle, a_NumSlots);
}
AString cProtocolRecognizer::GetAuthServerID(void)
{
ASSERT(m_Protocol != NULL);
return m_Protocol->GetAuthServerID();
}
void cProtocolRecognizer::SendData(const char * a_Data, int a_Size)
{
// This is used only when handling the server ping
m_Client->SendData(a_Data, a_Size);
}
bool cProtocolRecognizer::TryRecognizeProtocol(void)
{
// NOTE: If a new protocol is added or an old one is removed, adjust MCS_CLIENT_VERSIONS and
// MCS_PROTOCOL_VERSIONS macros in the header file, as well as PROTO_VERSION_LATEST macro
// The first packet should be a Handshake, 0x02:
unsigned char PacketType;
if (!m_Buffer.ReadByte(PacketType))
{
return false;
}
switch (PacketType)
{
case 0x02: break; // Handshake, continue recognizing
case 0xfe: HandleServerPing(); return false;
default: return false;
}
// 1.3.2 starts with 0x02 0x39 <name-length-short>
// 1.2.5 starts with 0x02 <name-length-short> and name is expected to less than 0x3900 long :)
char ch;
if (!m_Buffer.ReadChar(ch))
{
return false;
}
switch (ch)
{
case PROTO_VERSION_1_3_2:
{
m_Protocol = new cProtocol132(m_Client);
return true;
}
case PROTO_VERSION_1_4_2:
case PROTO_VERSION_1_4_4:
{
m_Protocol = new cProtocol142(m_Client);
return true;
}
case PROTO_VERSION_1_4_6:
{
m_Protocol = new cProtocol146(m_Client);
return true;
}
}
m_Protocol = new cProtocol125(m_Client);
return true;
}
void cProtocolRecognizer::HandleServerPing(void)
{
AString Reply;
switch (cRoot::Get()->m_PrimaryServerVersion)
{
case PROTO_VERSION_1_2_5:
case PROTO_VERSION_1_3_2:
{
// http://wiki.vg/wiki/index.php?title=Protocol&oldid=3099#Server_List_Ping_.280xFE.29
Printf(Reply, "%s%s%i%s%i",
cRoot::Get()->GetDefaultWorld()->GetDescription().c_str(),
cChatColor::Delimiter.c_str(),
cRoot::Get()->GetDefaultWorld()->GetNumPlayers(),
cChatColor::Delimiter.c_str(),
cRoot::Get()->GetDefaultWorld()->GetMaxPlayers()
);
break;
}
case PROTO_VERSION_1_4_2:
case PROTO_VERSION_1_4_4:
case PROTO_VERSION_1_4_6:
{
// The server list ping now has 1 more byte of "magic". Mojang just loves to complicate stuff.
// http://wiki.vg/wiki/index.php?title=Protocol&oldid=3101#Server_List_Ping_.280xFE.29
// _X 2012_10_31: I know that this needn't eat the byte, since it still may be in transit.
// Who cares? We're disconnecting anyway.
if (m_Buffer.CanReadBytes(1))
{
byte val;
m_Buffer.ReadByte(val);
ASSERT(val == 0x01);
}
// http://wiki.vg/wiki/index.php?title=Server_List_Ping&oldid=3100
AString NumPlayers;
Printf(NumPlayers, "%d", cRoot::Get()->GetDefaultWorld()->GetNumPlayers());
AString MaxPlayers;
Printf(MaxPlayers, "%d", cRoot::Get()->GetDefaultWorld()->GetMaxPlayers());
AString ProtocolVersionNum;
Printf(ProtocolVersionNum, "%d", cRoot::Get()->m_PrimaryServerVersion);
AString ProtocolVersionTxt(GetVersionTextFromInt(cRoot::Get()->m_PrimaryServerVersion));
// Cannot use Printf() because of in-string NUL bytes.
Reply = cChatColor::Delimiter;
Reply.append("1");
Reply.push_back(0);
Reply.append(ProtocolVersionNum);
Reply.push_back(0);
Reply.append(ProtocolVersionTxt);
Reply.push_back(0);
Reply.append(cRoot::Get()->GetDefaultWorld()->GetDescription());
Reply.push_back(0);
Reply.append(NumPlayers);
Reply.push_back(0);
Reply.append(MaxPlayers);
break;
}
} // switch (m_PrimaryServerVersion)
m_Client->Kick(Reply);
}