Moved source to src
This commit is contained in:
@@ -0,0 +1,176 @@
|
||||
|
||||
// ChunkDataSerializer.cpp
|
||||
|
||||
// Implements the cChunkDataSerializer class representing the object that can:
|
||||
// - serialize chunk data to different protocol versions
|
||||
// - cache such serialized data for multiple clients
|
||||
|
||||
#include "Globals.h"
|
||||
#include "ChunkDataSerializer.h"
|
||||
#include "zlib.h"
|
||||
|
||||
|
||||
|
||||
|
||||
cChunkDataSerializer::cChunkDataSerializer(
|
||||
const cChunkDef::BlockTypes & a_BlockTypes,
|
||||
const cChunkDef::BlockNibbles & a_BlockMetas,
|
||||
const cChunkDef::BlockNibbles & a_BlockLight,
|
||||
const cChunkDef::BlockNibbles & a_BlockSkyLight,
|
||||
const unsigned char * a_BiomeData
|
||||
) :
|
||||
m_BlockTypes(a_BlockTypes),
|
||||
m_BlockMetas(a_BlockMetas),
|
||||
m_BlockLight(a_BlockLight),
|
||||
m_BlockSkyLight(a_BlockSkyLight),
|
||||
m_BiomeData(a_BiomeData)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
const AString & cChunkDataSerializer::Serialize(int a_Version)
|
||||
{
|
||||
Serializations::const_iterator itr = m_Serializations.find(a_Version);
|
||||
if (itr != m_Serializations.end())
|
||||
{
|
||||
return itr->second;
|
||||
}
|
||||
|
||||
AString data;
|
||||
switch (a_Version)
|
||||
{
|
||||
case RELEASE_1_2_5: Serialize29(data); break;
|
||||
case RELEASE_1_3_2: Serialize39(data); break;
|
||||
// TODO: Other protocol versions may serialize the data differently; implement here
|
||||
|
||||
default:
|
||||
{
|
||||
LOGERROR("cChunkDataSerializer::Serialize(): Unknown version: %d", a_Version);
|
||||
ASSERT(!"Unknown chunk data serialization version");
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_Serializations[a_Version] = data;
|
||||
return m_Serializations[a_Version];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkDataSerializer::Serialize29(AString & a_Data)
|
||||
{
|
||||
// TODO: Do not copy data and then compress it; rather, compress partial blocks of data (zlib *can* stream)
|
||||
|
||||
const int BiomeDataSize = cChunkDef::Width * cChunkDef::Width;
|
||||
const int MetadataOffset = sizeof(m_BlockTypes);
|
||||
const int BlockLightOffset = MetadataOffset + sizeof(m_BlockMetas);
|
||||
const int SkyLightOffset = BlockLightOffset + sizeof(m_BlockLight);
|
||||
const int BiomeOffset = SkyLightOffset + sizeof(m_BlockSkyLight);
|
||||
const int DataSize = BiomeOffset + BiomeDataSize;
|
||||
|
||||
// Temporary buffer for the composed data:
|
||||
char AllData [DataSize];
|
||||
|
||||
memcpy(AllData, m_BlockTypes, sizeof(m_BlockTypes));
|
||||
memcpy(AllData + MetadataOffset, m_BlockMetas, sizeof(m_BlockMetas));
|
||||
memcpy(AllData + BlockLightOffset, m_BlockLight, sizeof(m_BlockLight));
|
||||
memcpy(AllData + SkyLightOffset, m_BlockSkyLight, sizeof(m_BlockSkyLight));
|
||||
memcpy(AllData + BiomeOffset, m_BiomeData, BiomeDataSize);
|
||||
|
||||
// Compress the data:
|
||||
// In order not to use allocation, use a fixed-size buffer, with the size
|
||||
// that uses the same calculation as compressBound():
|
||||
const uLongf CompressedMaxSize = DataSize + (DataSize >> 12) + (DataSize >> 14) + (DataSize >> 25) + 16;
|
||||
char CompressedBlockData[CompressedMaxSize];
|
||||
|
||||
uLongf CompressedSize = compressBound(DataSize);
|
||||
|
||||
// Run-time check that our compile-time guess about CompressedMaxSize was enough:
|
||||
ASSERT(CompressedSize <= CompressedMaxSize);
|
||||
|
||||
compress2((Bytef*)CompressedBlockData, &CompressedSize, (const Bytef*)AllData, sizeof(AllData), Z_DEFAULT_COMPRESSION);
|
||||
|
||||
// Now put all those data into a_Data:
|
||||
|
||||
// "Ground-up continuous", or rather, "biome data present" flag:
|
||||
a_Data.push_back('\x01');
|
||||
|
||||
// Two bitmaps; we're aways sending the full chunk with no additional data, so the bitmaps are 0xffff and 0, respectively
|
||||
// Also, no endian flipping is needed because of the const values
|
||||
unsigned short BitMap1 = 0xffff;
|
||||
unsigned short BitMap2 = 0;
|
||||
a_Data.append((const char *)&BitMap1, sizeof(short));
|
||||
a_Data.append((const char *)&BitMap2, sizeof(short));
|
||||
|
||||
Int32 CompressedSizeBE = htonl(CompressedSize);
|
||||
a_Data.append((const char *)&CompressedSizeBE, sizeof(CompressedSizeBE));
|
||||
|
||||
Int32 UnusedInt32 = 0;
|
||||
a_Data.append((const char *)&UnusedInt32, sizeof(UnusedInt32));
|
||||
|
||||
a_Data.append(CompressedBlockData, CompressedSize);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkDataSerializer::Serialize39(AString & a_Data)
|
||||
{
|
||||
// TODO: Do not copy data and then compress it; rather, compress partial blocks of data (zlib *can* stream)
|
||||
|
||||
const int BiomeDataSize = cChunkDef::Width * cChunkDef::Width;
|
||||
const int MetadataOffset = sizeof(m_BlockTypes);
|
||||
const int BlockLightOffset = MetadataOffset + sizeof(m_BlockMetas);
|
||||
const int SkyLightOffset = BlockLightOffset + sizeof(m_BlockLight);
|
||||
const int BiomeOffset = SkyLightOffset + sizeof(m_BlockSkyLight);
|
||||
const int DataSize = BiomeOffset + BiomeDataSize;
|
||||
|
||||
// Temporary buffer for the composed data:
|
||||
char AllData [DataSize];
|
||||
|
||||
memcpy(AllData, m_BlockTypes, sizeof(m_BlockTypes));
|
||||
memcpy(AllData + MetadataOffset, m_BlockMetas, sizeof(m_BlockMetas));
|
||||
memcpy(AllData + BlockLightOffset, m_BlockLight, sizeof(m_BlockLight));
|
||||
memcpy(AllData + SkyLightOffset, m_BlockSkyLight, sizeof(m_BlockSkyLight));
|
||||
memcpy(AllData + BiomeOffset, m_BiomeData, BiomeDataSize);
|
||||
|
||||
// Compress the data:
|
||||
// In order not to use allocation, use a fixed-size buffer, with the size
|
||||
// that uses the same calculation as compressBound():
|
||||
const uLongf CompressedMaxSize = DataSize + (DataSize >> 12) + (DataSize >> 14) + (DataSize >> 25) + 16;
|
||||
char CompressedBlockData[CompressedMaxSize];
|
||||
|
||||
uLongf CompressedSize = compressBound(DataSize);
|
||||
|
||||
// Run-time check that our compile-time guess about CompressedMaxSize was enough:
|
||||
ASSERT(CompressedSize <= CompressedMaxSize);
|
||||
|
||||
compress2((Bytef*)CompressedBlockData, &CompressedSize, (const Bytef*)AllData, sizeof(AllData), Z_DEFAULT_COMPRESSION);
|
||||
|
||||
// Now put all those data into a_Data:
|
||||
|
||||
// "Ground-up continuous", or rather, "biome data present" flag:
|
||||
a_Data.push_back('\x01');
|
||||
|
||||
// Two bitmaps; we're aways sending the full chunk with no additional data, so the bitmaps are 0xffff and 0, respectively
|
||||
// Also, no endian flipping is needed because of the const values
|
||||
unsigned short BitMap1 = 0xffff;
|
||||
unsigned short BitMap2 = 0;
|
||||
a_Data.append((const char *)&BitMap1, sizeof(short));
|
||||
a_Data.append((const char *)&BitMap2, sizeof(short));
|
||||
|
||||
Int32 CompressedSizeBE = htonl(CompressedSize);
|
||||
a_Data.append((const char *)&CompressedSizeBE, sizeof(CompressedSizeBE));
|
||||
|
||||
// Unlike 29, 39 doesn't have the "unused" int
|
||||
|
||||
a_Data.append(CompressedBlockData, CompressedSize);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
|
||||
// ChunkDataSerializer.h
|
||||
|
||||
// Interfaces to the cChunkDataSerializer class representing the object that can:
|
||||
// - serialize chunk data to different protocol versions
|
||||
// - cache such serialized data for multiple clients
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cChunkDataSerializer
|
||||
{
|
||||
protected:
|
||||
const cChunkDef::BlockTypes & m_BlockTypes;
|
||||
const cChunkDef::BlockNibbles & m_BlockMetas;
|
||||
const cChunkDef::BlockNibbles & m_BlockLight;
|
||||
const cChunkDef::BlockNibbles & m_BlockSkyLight;
|
||||
const unsigned char * m_BiomeData;
|
||||
|
||||
typedef std::map<int, AString> Serializations;
|
||||
|
||||
Serializations m_Serializations;
|
||||
|
||||
void Serialize29(AString & a_Data); // Release 1.2.4 and 1.2.5
|
||||
void Serialize39(AString & a_Data); // Release 1.3.1 and 1.3.2
|
||||
|
||||
public:
|
||||
enum
|
||||
{
|
||||
RELEASE_1_2_5 = 29,
|
||||
RELEASE_1_3_2 = 39,
|
||||
} ;
|
||||
|
||||
cChunkDataSerializer(
|
||||
const cChunkDef::BlockTypes & a_BlockTypes,
|
||||
const cChunkDef::BlockNibbles & a_BlockMetas,
|
||||
const cChunkDef::BlockNibbles & a_BlockLight,
|
||||
const cChunkDef::BlockNibbles & a_BlockSkyLight,
|
||||
const unsigned char * a_BiomeData
|
||||
);
|
||||
|
||||
const AString & Serialize(int a_Version); // Returns one of the internal m_Serializations[]
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
|
||||
// Protocol.h
|
||||
|
||||
// Interfaces to the cProtocol class representing the generic interface that a protocol
|
||||
// parser and serializer must implement
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Defines.h"
|
||||
#include "../Endianness.h"
|
||||
|
||||
|
||||
|
||||
|
||||
class cPlayer;
|
||||
class cEntity;
|
||||
class cWindow;
|
||||
class cInventory;
|
||||
class cPawn;
|
||||
class cPickup;
|
||||
class cMonster;
|
||||
class cChunkDataSerializer;
|
||||
class cWorld;
|
||||
class cFallingBlock;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
typedef unsigned char Byte;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cProtocol
|
||||
{
|
||||
public:
|
||||
cProtocol(cClientHandle * a_Client) :
|
||||
m_Client(a_Client)
|
||||
{
|
||||
}
|
||||
virtual ~cProtocol() {}
|
||||
|
||||
/// Called when client sends some data
|
||||
virtual void DataReceived(const char * a_Data, int a_Size) = 0;
|
||||
|
||||
// Sending stuff to clients (alphabetically sorted):
|
||||
virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) = 0;
|
||||
virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) = 0;
|
||||
virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) = 0;
|
||||
virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0;
|
||||
virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) = 0;
|
||||
virtual void SendChat (const AString & a_Message) = 0;
|
||||
virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) = 0;
|
||||
virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) = 0;
|
||||
virtual void SendDestroyEntity (const cEntity & a_Entity) = 0;
|
||||
virtual void SendDisconnect (const AString & a_Reason) = 0;
|
||||
virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) = 0; ///< Request the client to open up the sign editor for the sign (1.6+)
|
||||
virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) = 0;
|
||||
virtual void SendEntityHeadLook (const cEntity & a_Entity) = 0;
|
||||
virtual void SendEntityLook (const cEntity & a_Entity) = 0;
|
||||
virtual void SendEntityMetadata (const cEntity & a_Entity) = 0;
|
||||
virtual void SendEntityProperties (const cEntity & a_Entity) = 0;
|
||||
virtual void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) = 0;
|
||||
virtual void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) = 0;
|
||||
virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) = 0;
|
||||
virtual void SendEntityVelocity (const cEntity & a_Entity) = 0;
|
||||
virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) = 0;
|
||||
virtual void SendGameMode (eGameMode a_GameMode) = 0;
|
||||
virtual void SendHealth (void) = 0;
|
||||
virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) = 0;
|
||||
virtual void SendKeepAlive (int a_PingID) = 0;
|
||||
virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) = 0;
|
||||
virtual void SendPickupSpawn (const cPickup & a_Pickup) = 0;
|
||||
virtual void SendPlayerAbilities (void) = 0;
|
||||
virtual void SendPlayerAnimation (const cPlayer & a_Player, char a_Animation) = 0;
|
||||
virtual void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline) = 0;
|
||||
virtual void SendPlayerMaxSpeed (void) = 0; ///< Informs the client of the maximum player speed (1.6.1+)
|
||||
virtual void SendPlayerMoveLook (void) = 0;
|
||||
virtual void SendPlayerPosition (void) = 0;
|
||||
virtual void SendPlayerSpawn (const cPlayer & a_Player) = 0;
|
||||
virtual void SendRespawn (void) = 0;
|
||||
virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) = 0; // a_Src coords are Block * 8
|
||||
virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) = 0;
|
||||
virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) = 0;
|
||||
virtual void SendSpawnMob (const cMonster & a_Mob) = 0;
|
||||
virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) = 0;
|
||||
virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) = 0;
|
||||
virtual void SendTabCompletionResults(const AStringVector & a_Results) = 0;
|
||||
virtual void SendTeleportEntity (const cEntity & a_Entity) = 0;
|
||||
virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) = 0;
|
||||
virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) = 0;
|
||||
virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) = 0;
|
||||
virtual void 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) = 0;
|
||||
virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) = 0;
|
||||
virtual void SendWeather (eWeather a_Weather) = 0;
|
||||
virtual void SendWholeInventory (const cWindow & a_Window) = 0;
|
||||
virtual void SendWindowClose (const cWindow & a_Window) = 0;
|
||||
virtual void SendWindowOpen (const cWindow & a_Window) = 0;
|
||||
virtual void SendWindowProperty (const cWindow & a_Window, short a_Property, short a_Value) = 0;
|
||||
|
||||
/// Returns the ServerID used for authentication through session.minecraft.net
|
||||
virtual AString GetAuthServerID(void) = 0;
|
||||
|
||||
protected:
|
||||
cClientHandle * m_Client;
|
||||
cCriticalSection m_CSPacket; //< Each SendXYZ() function must acquire this CS in order to send the whole packet at once
|
||||
|
||||
/// A generic data-sending routine, all outgoing packet data needs to be routed through this so that descendants may override it
|
||||
virtual void SendData(const char * a_Data, int a_Size) = 0;
|
||||
|
||||
/// Called after writing each packet, enables descendants to flush their buffers
|
||||
virtual void Flush(void) {};
|
||||
|
||||
// Helpers for writing partial packet data, write using SendData()
|
||||
void WriteByte(Byte a_Value)
|
||||
{
|
||||
SendData((const char *)&a_Value, 1);
|
||||
}
|
||||
|
||||
void WriteShort(short a_Value)
|
||||
{
|
||||
a_Value = htons(a_Value);
|
||||
SendData((const char *)&a_Value, 2);
|
||||
}
|
||||
|
||||
/*
|
||||
void WriteShort(unsigned short a_Value)
|
||||
{
|
||||
a_Value = htons(a_Value);
|
||||
SendData((const char *)&a_Value, 2);
|
||||
}
|
||||
*/
|
||||
|
||||
void WriteInt(int a_Value)
|
||||
{
|
||||
a_Value = htonl(a_Value);
|
||||
SendData((const char *)&a_Value, 4);
|
||||
}
|
||||
|
||||
void WriteUInt(unsigned int a_Value)
|
||||
{
|
||||
a_Value = htonl(a_Value);
|
||||
SendData((const char *)&a_Value, 4);
|
||||
}
|
||||
|
||||
void WriteInt64 (Int64 a_Value)
|
||||
{
|
||||
a_Value = HostToNetwork8(&a_Value);
|
||||
SendData((const char *)&a_Value, 8);
|
||||
}
|
||||
|
||||
void WriteFloat (float a_Value)
|
||||
{
|
||||
unsigned int val = HostToNetwork4(&a_Value);
|
||||
SendData((const char *)&val, 4);
|
||||
}
|
||||
|
||||
void WriteDouble(double a_Value)
|
||||
{
|
||||
unsigned long long val = HostToNetwork8(&a_Value);
|
||||
SendData((const char *)&val, 8);
|
||||
}
|
||||
|
||||
void WriteString(const AString & a_Value)
|
||||
{
|
||||
AString UTF16;
|
||||
UTF8ToRawBEUTF16(a_Value.c_str(), a_Value.length(), UTF16);
|
||||
WriteShort((unsigned short)(UTF16.size() / 2));
|
||||
SendData(UTF16.data(), UTF16.size());
|
||||
}
|
||||
|
||||
void WriteBool(bool a_Value)
|
||||
{
|
||||
WriteByte(a_Value ? 1 : 0);
|
||||
}
|
||||
|
||||
void WriteVectorI(const Vector3i & a_Vector)
|
||||
{
|
||||
WriteInt(a_Vector.x);
|
||||
WriteInt(a_Vector.y);
|
||||
WriteInt(a_Vector.z);
|
||||
}
|
||||
|
||||
void WriteVarInt(UInt32 a_Value)
|
||||
{
|
||||
// A 32-bit integer can be encoded by at most 5 bytes:
|
||||
unsigned char b[5];
|
||||
int idx = 0;
|
||||
do
|
||||
{
|
||||
b[idx] = (a_Value & 0x7f) | ((a_Value > 0x7f) ? 0x80 : 0x00);
|
||||
a_Value = a_Value >> 7;
|
||||
idx++;
|
||||
} while (a_Value > 0);
|
||||
|
||||
SendData((const char *)b, idx);
|
||||
}
|
||||
|
||||
void WriteVarUTF8String(const AString & a_String)
|
||||
{
|
||||
WriteVarInt(a_String.size());
|
||||
SendData(a_String.data(), a_String.size());
|
||||
}
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,157 @@
|
||||
|
||||
// Protocol125.h
|
||||
|
||||
// Interfaces to the cProtocol125 class representing the release 1.2.5 protocol (#29)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Protocol.h"
|
||||
#include "../ByteBuffer.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cProtocol125 :
|
||||
public cProtocol
|
||||
{
|
||||
typedef cProtocol super;
|
||||
public:
|
||||
cProtocol125(cClientHandle * a_Client);
|
||||
|
||||
/// Called when client sends some data:
|
||||
virtual void DataReceived(const char * a_Data, int a_Size) override;
|
||||
|
||||
/// Sending stuff to clients (alphabetically sorted):
|
||||
virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override;
|
||||
virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override;
|
||||
virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override;
|
||||
virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
|
||||
virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override;
|
||||
virtual void SendChat (const AString & a_Message) override;
|
||||
virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override;
|
||||
virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override;
|
||||
virtual void SendDestroyEntity (const cEntity & a_Entity) override;
|
||||
virtual void SendDisconnect (const AString & a_Reason) override;
|
||||
virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) override; ///< Request the client to open up the sign editor for the sign (1.6+)
|
||||
virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override;
|
||||
virtual void SendEntityHeadLook (const cEntity & a_Entity) override;
|
||||
virtual void SendEntityLook (const cEntity & a_Entity) override;
|
||||
virtual void SendEntityMetadata (const cEntity & a_Entity) override;
|
||||
virtual void SendEntityProperties (const cEntity & a_Entity) override;
|
||||
virtual void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override;
|
||||
virtual void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override;
|
||||
virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) override;
|
||||
virtual void SendEntityVelocity (const cEntity & a_Entity) override;
|
||||
virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) override;
|
||||
virtual void SendGameMode (eGameMode a_GameMode) override;
|
||||
virtual void SendHealth (void) override;
|
||||
virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override;
|
||||
virtual void SendKeepAlive (int a_PingID) override;
|
||||
virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override;
|
||||
virtual void SendPickupSpawn (const cPickup & a_Pickup) override;
|
||||
virtual void SendPlayerAbilities (void) override {} // This protocol doesn't support such message
|
||||
virtual void SendPlayerAnimation (const cPlayer & a_Player, char a_Animation) override;
|
||||
virtual void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline) override;
|
||||
virtual void SendPlayerMaxSpeed (void) override;
|
||||
virtual void SendPlayerMoveLook (void) override;
|
||||
virtual void SendPlayerPosition (void) override;
|
||||
virtual void SendPlayerSpawn (const cPlayer & a_Player) override;
|
||||
virtual void SendRespawn (void) override;
|
||||
virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; // a_Src coords are Block * 8
|
||||
virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override;
|
||||
virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override;
|
||||
virtual void SendSpawnMob (const cMonster & a_Mob) override;
|
||||
virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) override;
|
||||
virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) override;
|
||||
virtual void SendTabCompletionResults(const AStringVector & a_Results) override;
|
||||
virtual void SendTeleportEntity (const cEntity & a_Entity) override;
|
||||
virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override;
|
||||
virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) override;
|
||||
virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override;
|
||||
virtual void 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) override;
|
||||
virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) override;
|
||||
virtual void SendWeather (eWeather a_Weather) override;
|
||||
virtual void SendWholeInventory (const cWindow & a_Window) override;
|
||||
virtual void SendWindowClose (const cWindow & a_Window) override;
|
||||
virtual void SendWindowOpen (const cWindow & a_Window) override;
|
||||
virtual void SendWindowProperty (const cWindow & a_Window, short a_Property, short a_Value) override;
|
||||
|
||||
virtual AString GetAuthServerID(void) override;
|
||||
|
||||
protected:
|
||||
/// Results of packet-parsing:
|
||||
enum {
|
||||
PARSE_OK = 1,
|
||||
PARSE_ERROR = -1,
|
||||
PARSE_UNKNOWN = -2,
|
||||
PARSE_INCOMPLETE = -3,
|
||||
} ;
|
||||
|
||||
cByteBuffer m_ReceivedData; ///< Buffer for the received data
|
||||
|
||||
AString m_Username; ///< Stored in ParseHandshake(), compared to Login username
|
||||
|
||||
virtual void SendData(const char * a_Data, int a_Size) override;
|
||||
|
||||
/// Sends the Handshake packet
|
||||
void SendHandshake(const AString & a_ConnectionHash);
|
||||
|
||||
/// Parse the packet of the specified type from m_ReceivedData (switch into ParseXYZ() )
|
||||
virtual int ParsePacket(unsigned char a_PacketType);
|
||||
|
||||
// Specific packet parsers:
|
||||
virtual int ParseArmAnim (void);
|
||||
virtual int ParseBlockDig (void);
|
||||
virtual int ParseBlockPlace (void);
|
||||
virtual int ParseChat (void);
|
||||
virtual int ParseCreativeInventoryAction(void);
|
||||
virtual int ParseDisconnect (void);
|
||||
virtual int ParseEntityAction (void);
|
||||
virtual int ParseHandshake (void);
|
||||
virtual int ParseKeepAlive (void);
|
||||
virtual int ParseLogin (void);
|
||||
virtual int ParsePing (void);
|
||||
virtual int ParsePlayerAbilities (void);
|
||||
virtual int ParsePlayerLook (void);
|
||||
virtual int ParsePlayerMoveLook (void);
|
||||
virtual int ParsePlayerOnGround (void);
|
||||
virtual int ParsePlayerPosition (void);
|
||||
virtual int ParsePluginMessage (void);
|
||||
virtual int ParseRespawn (void);
|
||||
virtual int ParseSlotSelected (void);
|
||||
virtual int ParseUpdateSign (void);
|
||||
virtual int ParseUseEntity (void);
|
||||
virtual int ParseWindowClick (void);
|
||||
virtual int ParseWindowClose (void);
|
||||
|
||||
// Utility functions:
|
||||
/// Writes a "pre-chunk" packet
|
||||
void SendPreChunk(int a_ChunkX, int a_ChunkZ, bool a_ShouldLoad);
|
||||
|
||||
/// Writes a "set window items" packet with the specified params
|
||||
void SendWindowSlots(char a_WindowID, int a_NumItems, const cItem * a_Items);
|
||||
|
||||
/// Writes one item, "slot" as the protocol wiki calls it
|
||||
virtual void WriteItem(const cItem & a_Item);
|
||||
|
||||
/// Parses one item, "slot" as the protocol wiki calls it, from m_ReceivedData; returns the usual ParsePacket() codes
|
||||
virtual int ParseItem(cItem & a_Item);
|
||||
|
||||
/// Writes the COMMON entity metadata
|
||||
void WriteCommonMetadata(const cEntity & a_Entity);
|
||||
|
||||
/// Writes normal entity metadata
|
||||
void WriteEntityMetadata(const cEntity & a_Entity);
|
||||
|
||||
/// Writes mobile entity metadata
|
||||
void WriteMobMetadata(const cMonster & a_Mob);
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,950 @@
|
||||
|
||||
// Protocol132.cpp
|
||||
|
||||
// Implements the cProtocol132 class representing the release 1.3.2 protocol (#39)
|
||||
|
||||
#include "Globals.h"
|
||||
#include "Protocol132.h"
|
||||
#include "../Root.h"
|
||||
#include "../Server.h"
|
||||
#include "../World.h"
|
||||
#include "../ClientHandle.h"
|
||||
#include "../../CryptoPP/randpool.h"
|
||||
#include "../Item.h"
|
||||
#include "ChunkDataSerializer.h"
|
||||
#include "../Entities/Player.h"
|
||||
#include "../Mobs/Monster.h"
|
||||
#include "../UI/Window.h"
|
||||
#include "../Entities/Pickup.h"
|
||||
#include "../WorldStorage/FastNBT.h"
|
||||
#include "../StringCompression.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#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;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
using namespace CryptoPP;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const int MAX_ENC_LEN = 512; // Maximum size of the encrypted message; should be 128, but who knows...
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
PACKET_KEEP_ALIVE = 0x00,
|
||||
PACKET_LOGIN = 0x01,
|
||||
PACKET_ENTITY_EQUIPMENT = 0x05,
|
||||
PACKET_COMPASS = 0x06,
|
||||
PACKET_PLAYER_SPAWN = 0x14,
|
||||
PACKET_COLLECT_PICKUP = 0x16,
|
||||
PACKET_SPAWN_MOB = 0x18,
|
||||
PACKET_DESTROY_ENTITIES = 0x1d,
|
||||
PACKET_CHUNK_DATA = 0x33,
|
||||
PACKET_BLOCK_CHANGE = 0x35,
|
||||
PACKET_BLOCK_ACTION = 0x36,
|
||||
PACKET_BLOCK_BREAK_ANIM = 0x37,
|
||||
PACKET_SOUND_EFFECT = 0x3e,
|
||||
PACKET_SOUND_PARTICLE_EFFECT = 0x3d,
|
||||
PACKET_TAB_COMPLETION = 0xcb,
|
||||
PACKET_LOCALE_VIEW_DISTANCE = 0xcc,
|
||||
PACKET_CLIENT_STATUSES = 0xcd,
|
||||
PACKET_ENCRYPTION_KEY_RESP = 0xfc,
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Converts a raw 160-bit SHA1 digest into a Java Hex representation
|
||||
// According to http://wiki.vg/wiki/index.php?title=Protocol_Encryption&oldid=2802
|
||||
static void DigestToJava(byte a_Digest[20], AString & a_Out)
|
||||
{
|
||||
bool IsNegative = (a_Digest[0] >= 0x80);
|
||||
if (IsNegative)
|
||||
{
|
||||
// Two's complement:
|
||||
bool carry = true; // Add one to the whole number
|
||||
for (int i = 19; i >= 0; i--)
|
||||
{
|
||||
a_Digest[i] = ~a_Digest[i];
|
||||
if (carry)
|
||||
{
|
||||
carry = (a_Digest[i] == 0xff);
|
||||
a_Digest[i]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
a_Out.clear();
|
||||
a_Out.reserve(40);
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
AppendPrintf(a_Out, "%02x", a_Digest[i]);
|
||||
}
|
||||
while ((a_Out.length() > 0) && (a_Out[0] == '0'))
|
||||
{
|
||||
a_Out.erase(0, 1);
|
||||
}
|
||||
if (IsNegative)
|
||||
{
|
||||
a_Out.insert(0, "-");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
// Self-test the hash formatting for known values:
|
||||
// sha1(Notch) : 4ed1f46bbe04bc756bcb17c0c7ce3e4632f06a48
|
||||
// sha1(jeb_) : -7c9d5b0044c130109a5d7b5fb5c317c02b4e28c1
|
||||
// sha1(simon) : 88e16a1019277b15d58faf0541e11910eb756f6
|
||||
|
||||
class Test
|
||||
{
|
||||
public:
|
||||
Test(void)
|
||||
{
|
||||
AString DigestNotch, DigestJeb, DigestSimon;
|
||||
byte Digest[20];
|
||||
CryptoPP::SHA1 Checksum;
|
||||
Checksum.Update((const byte *)"Notch", 5);
|
||||
Checksum.Final(Digest);
|
||||
DigestToJava(Digest, DigestNotch);
|
||||
Checksum.Restart();
|
||||
Checksum.Update((const byte *)"jeb_", 4);
|
||||
Checksum.Final(Digest);
|
||||
DigestToJava(Digest, DigestJeb);
|
||||
Checksum.Restart();
|
||||
Checksum.Update((const byte *)"simon", 5);
|
||||
Checksum.Final(Digest);
|
||||
DigestToJava(Digest, DigestSimon);
|
||||
printf("Notch: \"%s\"", DigestNotch.c_str());
|
||||
printf("jeb_: \"%s\"", DigestJeb.c_str());
|
||||
printf("simon: \"%s\"", DigestSimon.c_str());
|
||||
}
|
||||
} test;
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cProtocol132:
|
||||
|
||||
cProtocol132::cProtocol132(cClientHandle * a_Client) :
|
||||
super(a_Client),
|
||||
m_IsEncrypted(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cProtocol132::~cProtocol132()
|
||||
{
|
||||
if (!m_DataToSend.empty())
|
||||
{
|
||||
LOGD("There are %d unsent bytes while deleting cProtocol132", m_DataToSend.size());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocol132::DataReceived(const char * a_Data, int a_Size)
|
||||
{
|
||||
if (m_IsEncrypted)
|
||||
{
|
||||
byte Decrypted[512];
|
||||
while (a_Size > 0)
|
||||
{
|
||||
int NumBytes = (a_Size > sizeof(Decrypted)) ? sizeof(Decrypted) : a_Size;
|
||||
m_Decryptor.ProcessData(Decrypted, (byte *)a_Data, NumBytes);
|
||||
super::DataReceived((const char *)Decrypted, NumBytes);
|
||||
a_Size -= NumBytes;
|
||||
a_Data += NumBytes;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
super::DataReceived(a_Data, a_Size);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocol132::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE 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);
|
||||
WriteShort(a_BlockType);
|
||||
Flush();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocol132::SendBlockBreakAnim(int a_entityID, int a_BlockX, int a_BlockY, int a_BlockZ, char stage)
|
||||
{
|
||||
cCSLock Lock(m_CSPacket);
|
||||
WriteByte (PACKET_BLOCK_BREAK_ANIM);
|
||||
WriteInt (a_entityID);
|
||||
WriteInt (a_BlockX);
|
||||
WriteInt (a_BlockY);
|
||||
WriteInt (a_BlockZ);
|
||||
WriteByte (stage);
|
||||
Flush();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocol132::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);
|
||||
WriteShort(a_BlockType);
|
||||
WriteByte (a_BlockMeta);
|
||||
Flush();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocol132::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer)
|
||||
{
|
||||
cCSLock Lock(m_CSPacket);
|
||||
|
||||
// Pre-chunk not used in 1.3.2. Finally.
|
||||
|
||||
// Send the chunk data:
|
||||
AString Serialized = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_3_2);
|
||||
WriteByte(PACKET_CHUNK_DATA);
|
||||
WriteInt (a_ChunkX);
|
||||
WriteInt (a_ChunkZ);
|
||||
SendData(Serialized.data(), Serialized.size());
|
||||
Flush();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocol132::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();
|
||||
|
||||
// Also send the "pop" sound effect with a somewhat random pitch (fast-random using EntityID ;)
|
||||
SendSoundEffect(
|
||||
"random.pop",
|
||||
(int)(a_Pickup.GetPosX() * 8), (int)(a_Pickup.GetPosY() * 8), (int)(a_Pickup.GetPosZ() * 8),
|
||||
0.5, (float)(0.75 + ((float)((a_Pickup.GetUniqueID() * 23) % 32)) / 64)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocol132::SendDestroyEntity(const cEntity & a_Entity)
|
||||
{
|
||||
if (a_Entity.GetUniqueID() == m_Client->GetPlayer()->GetUniqueID())
|
||||
{
|
||||
// Do not send "destroy self" to the client, the client would crash (FS #254)
|
||||
return;
|
||||
}
|
||||
|
||||
cCSLock Lock(m_CSPacket);
|
||||
WriteByte(PACKET_DESTROY_ENTITIES);
|
||||
WriteByte(1); // entity count
|
||||
WriteInt (a_Entity.GetUniqueID());
|
||||
Flush();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocol132::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);
|
||||
WriteItem (a_Item);
|
||||
Flush();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocol132::SendLogin(const cPlayer & a_Player, const cWorld & a_World)
|
||||
{
|
||||
cCSLock Lock(m_CSPacket);
|
||||
WriteByte (PACKET_LOGIN);
|
||||
WriteInt (a_Player.GetUniqueID()); // EntityID of the player
|
||||
WriteString("default"); // Level type
|
||||
WriteByte ((int)a_Player.GetGameMode());
|
||||
WriteByte ((Byte)(a_World.GetDimension()));
|
||||
WriteByte (2); // TODO: Difficulty
|
||||
WriteByte (0); // Unused, used to be world height
|
||||
WriteByte (8); // Client list width or something
|
||||
Flush();
|
||||
|
||||
SendCompass(a_World);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocol132::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);
|
||||
// Player metadata: just use a default metadata value, since the client doesn't like starting without any metadata:
|
||||
WriteByte (0); // Index 0, byte (flags)
|
||||
WriteByte (0); // Flags, empty
|
||||
WriteByte (0x7f); // End of metadata
|
||||
Flush();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocol132::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch)
|
||||
{
|
||||
cCSLock Lock(m_CSPacket);
|
||||
WriteByte (PACKET_SOUND_EFFECT);
|
||||
WriteString (a_SoundName);
|
||||
WriteInt (a_SrcX);
|
||||
WriteInt (a_SrcY);
|
||||
WriteInt (a_SrcZ);
|
||||
WriteFloat (a_Volume);
|
||||
WriteByte ((char)(a_Pitch * 63.0f));
|
||||
Flush();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocol132::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data)
|
||||
{
|
||||
cCSLock Lock(m_CSPacket);
|
||||
WriteByte(PACKET_SOUND_PARTICLE_EFFECT);
|
||||
WriteInt (a_EffectID);
|
||||
WriteInt (a_SrcX);
|
||||
WriteByte(a_SrcY);
|
||||
WriteInt (a_SrcZ);
|
||||
WriteInt (a_Data);
|
||||
Flush();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocol132::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 ((Byte)((a_Mob.GetRotation() / 360.f) * 256));
|
||||
WriteByte ((Byte)((a_Mob.GetPitch() / 360.f) * 256));
|
||||
WriteByte ((Byte)((a_Mob.GetHeadYaw() / 360.f) * 256));
|
||||
WriteShort ((short)(a_Mob.GetSpeedX() * 400));
|
||||
WriteShort ((short)(a_Mob.GetSpeedY() * 400));
|
||||
WriteShort ((short)(a_Mob.GetSpeedZ() * 400));
|
||||
|
||||
WriteCommonMetadata(a_Mob);
|
||||
WriteMobMetadata(a_Mob);
|
||||
WriteByte(0x7f);
|
||||
|
||||
Flush();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocol132::SendTabCompletionResults(const AStringVector & a_Results)
|
||||
{
|
||||
if (a_Results.empty())
|
||||
{
|
||||
// No results to send
|
||||
return;
|
||||
}
|
||||
|
||||
AString Serialized(a_Results[0]);
|
||||
for (AStringVector::const_iterator itr = a_Results.begin() + 1, end = a_Results.end(); itr != end; ++itr)
|
||||
{
|
||||
Serialized.push_back(0);
|
||||
Serialized.append(*itr);
|
||||
} // for itr - a_Results[]
|
||||
|
||||
cCSLock Lock(m_CSPacket);
|
||||
WriteByte(PACKET_TAB_COMPLETION);
|
||||
WriteString(Serialized);
|
||||
Flush();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocol132::SendUnloadChunk(int a_ChunkX, int a_ChunkZ)
|
||||
{
|
||||
// Unloading the chunk is done by sending a "map chunk" packet
|
||||
// with IncludeInitialize set to true and primary bitmap set to 0:
|
||||
cCSLock Lock(m_CSPacket);
|
||||
WriteByte(PACKET_CHUNK_DATA);
|
||||
WriteInt (a_ChunkX);
|
||||
WriteInt (a_ChunkZ);
|
||||
WriteBool(true); // IncludeInitialize
|
||||
WriteShort(0); // Primary bitmap
|
||||
WriteShort(0); // Add bitmap
|
||||
WriteInt(0);
|
||||
Flush();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocol132::SendWholeInventory(const cWindow & a_Window)
|
||||
{
|
||||
// 1.3.2 requires player inventory slots to be sent as SetSlot packets,
|
||||
// otherwise it sometimes fails to update the window
|
||||
|
||||
// Send the entire window:
|
||||
super::SendWholeInventory(a_Window);
|
||||
|
||||
// Send the player inventory and hotbar:
|
||||
const cInventory & Inventory = m_Client->GetPlayer()->GetInventory();
|
||||
int BaseOffset = a_Window.GetNumSlots() - (cInventory::invNumSlots - cInventory::invInventoryOffset); // Number of non-inventory slots
|
||||
char WindowID = a_Window.GetWindowID();
|
||||
for (int i = 0; i < cInventory::invInventoryCount; i++)
|
||||
{
|
||||
SendInventorySlot(WindowID, BaseOffset + i, Inventory.GetInventorySlot(i));
|
||||
} // for i - Inventory[]
|
||||
BaseOffset += cInventory::invInventoryCount;
|
||||
for (int i = 0; i < cInventory::invHotbarCount; i++)
|
||||
{
|
||||
SendInventorySlot(WindowID, BaseOffset + i, Inventory.GetHotbarSlot(i));
|
||||
} // for i - Hotbar[]
|
||||
|
||||
// Send even the item being dragged:
|
||||
SendInventorySlot(-1, -1, m_Client->GetPlayer()->GetDraggingItem());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
AString cProtocol132::GetAuthServerID(void)
|
||||
{
|
||||
// http://wiki.vg/wiki/index.php?title=Session&oldid=2615
|
||||
// Server uses SHA1 to mix ServerID, Client secret and server public key together
|
||||
// The mixing is done in StartEncryption, the result is in m_AuthServerID
|
||||
|
||||
return m_AuthServerID;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cProtocol132::ParsePacket(unsigned char a_PacketType)
|
||||
{
|
||||
switch (a_PacketType)
|
||||
{
|
||||
default: return super::ParsePacket(a_PacketType); // off-load previously known packets into cProtocol125
|
||||
case PACKET_CLIENT_STATUSES: return ParseClientStatuses();
|
||||
case PACKET_ENCRYPTION_KEY_RESP: return ParseEncryptionKeyResponse();
|
||||
case PACKET_LOCALE_VIEW_DISTANCE: return ParseLocaleViewDistance();
|
||||
case PACKET_TAB_COMPLETION: return ParseTabCompletion();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cProtocol132::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;
|
||||
}
|
||||
|
||||
HANDLE_PACKET_READ(ReadChar, char, CursorX);
|
||||
HANDLE_PACKET_READ(ReadChar, char, CursorY);
|
||||
HANDLE_PACKET_READ(ReadChar, char, CursorZ);
|
||||
|
||||
m_Client->HandleRightClick(PosX, PosY, PosZ, BlockFace, CursorX, CursorY, CursorZ, HeldItem);
|
||||
return PARSE_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cProtocol132::ParseHandshake(void)
|
||||
{
|
||||
HANDLE_PACKET_READ(ReadByte, Byte, ProtocolVersion);
|
||||
HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Username);
|
||||
HANDLE_PACKET_READ(ReadBEUTF16String16, AString, ServerHost);
|
||||
HANDLE_PACKET_READ(ReadBEInt, int, ServerPort);
|
||||
m_Username = Username;
|
||||
|
||||
if (!m_Client->HandleHandshake( m_Username ))
|
||||
{
|
||||
return PARSE_OK; // Player is not allowed into the server
|
||||
}
|
||||
|
||||
// Send a 0xFD Encryption Key Request http://wiki.vg/Protocol#0xFD
|
||||
CryptoPP::StringSink sink(m_ServerPublicKey); // GCC won't allow inline instantiation in the following line, damned temporary refs
|
||||
cRoot::Get()->GetServer()->GetPublicKey().Save(sink);
|
||||
SendEncryptionKeyRequest();
|
||||
|
||||
return PARSE_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cProtocol132::ParseClientStatuses(void)
|
||||
{
|
||||
HANDLE_PACKET_READ(ReadByte, byte, Status);
|
||||
if ((Status & 1) == 0)
|
||||
{
|
||||
m_Client->HandleLogin(39, m_Username);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Client->HandleRespawn();
|
||||
}
|
||||
return PARSE_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cProtocol132::ParseEncryptionKeyResponse(void)
|
||||
{
|
||||
HANDLE_PACKET_READ(ReadBEShort, short, EncKeyLength);
|
||||
AString EncKey;
|
||||
if (!m_ReceivedData.ReadString(EncKey, EncKeyLength))
|
||||
{
|
||||
return PARSE_INCOMPLETE;
|
||||
}
|
||||
HANDLE_PACKET_READ(ReadBEShort, short, EncNonceLength);
|
||||
AString EncNonce;
|
||||
if (!m_ReceivedData.ReadString(EncNonce, EncNonceLength))
|
||||
{
|
||||
return PARSE_INCOMPLETE;
|
||||
}
|
||||
if ((EncKeyLength > MAX_ENC_LEN) || (EncNonceLength > MAX_ENC_LEN))
|
||||
{
|
||||
LOGD("Too long encryption");
|
||||
m_Client->Kick("Hacked client");
|
||||
return PARSE_OK;
|
||||
}
|
||||
|
||||
HandleEncryptionKeyResponse(EncKey, EncNonce);
|
||||
return PARSE_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cProtocol132::ParseLocaleViewDistance(void)
|
||||
{
|
||||
HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Locale);
|
||||
HANDLE_PACKET_READ(ReadChar, char, ViewDistance);
|
||||
HANDLE_PACKET_READ(ReadChar, char, ChatFlags);
|
||||
HANDLE_PACKET_READ(ReadChar, char, ClientDifficulty);
|
||||
// TODO: m_Client->HandleLocale(Locale);
|
||||
// TODO: m_Client->HandleViewDistance(ViewDistance);
|
||||
// TODO: m_Client->HandleChatFlags(ChatFlags);
|
||||
// Ignoring client difficulty
|
||||
return PARSE_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cProtocol132::ParseLogin(void)
|
||||
{
|
||||
// Login packet not used in 1.3.2
|
||||
return PARSE_ERROR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cProtocol132::ParsePlayerAbilities(void)
|
||||
{
|
||||
HANDLE_PACKET_READ(ReadBool, bool, Flags);
|
||||
HANDLE_PACKET_READ(ReadChar, char, FlyingSpeed);
|
||||
HANDLE_PACKET_READ(ReadChar, char, WalkingSpeed);
|
||||
// TODO: m_Client->HandlePlayerAbilities(...);
|
||||
return PARSE_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cProtocol132::ParseTabCompletion(void)
|
||||
{
|
||||
HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Text);
|
||||
m_Client->HandleTabCompletion(Text);
|
||||
return PARSE_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocol132::SendData(const char * a_Data, int a_Size)
|
||||
{
|
||||
m_DataToSend.append(a_Data, a_Size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocol132::Flush(void)
|
||||
{
|
||||
ASSERT(m_CSPacket.IsLockedByCurrentThread()); // Did all packets lock the CS properly?
|
||||
|
||||
if (m_DataToSend.empty())
|
||||
{
|
||||
LOGD("Flushing empty");
|
||||
return;
|
||||
}
|
||||
const char * a_Data = m_DataToSend.data();
|
||||
int a_Size = m_DataToSend.size();
|
||||
if (m_IsEncrypted)
|
||||
{
|
||||
byte Encrypted[8192]; // Larger buffer, we may be sending lots of data (chunks)
|
||||
while (a_Size > 0)
|
||||
{
|
||||
int NumBytes = (a_Size > sizeof(Encrypted)) ? sizeof(Encrypted) : a_Size;
|
||||
m_Encryptor.ProcessData(Encrypted, (byte *)a_Data, NumBytes);
|
||||
super::SendData((const char *)Encrypted, NumBytes);
|
||||
a_Size -= NumBytes;
|
||||
a_Data += NumBytes;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
super::SendData(a_Data, a_Size);
|
||||
}
|
||||
m_DataToSend.clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocol132::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;
|
||||
}
|
||||
|
||||
if (a_Item.IsEmpty())
|
||||
{
|
||||
WriteShort(-1);
|
||||
return;
|
||||
}
|
||||
|
||||
WriteShort(ItemType);
|
||||
WriteByte (a_Item.m_ItemCount);
|
||||
WriteShort(a_Item.m_ItemDamage);
|
||||
|
||||
if (a_Item.m_Enchantments.IsEmpty())
|
||||
{
|
||||
WriteShort(-1);
|
||||
return;
|
||||
}
|
||||
|
||||
// Send the enchantments:
|
||||
cFastNBTWriter Writer;
|
||||
const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench";
|
||||
a_Item.m_Enchantments.WriteToNBTCompound(Writer, TagName);
|
||||
Writer.Finish();
|
||||
AString Compressed;
|
||||
CompressStringGZIP(Writer.GetResult().data(), Writer.GetResult().size(), Compressed);
|
||||
WriteShort(Compressed.size());
|
||||
SendData(Compressed.data(), Compressed.size());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cProtocol132::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();
|
||||
}
|
||||
|
||||
HANDLE_PACKET_READ(ReadBEShort, short, MetadataLength);
|
||||
if (MetadataLength <= 0)
|
||||
{
|
||||
return PARSE_OK;
|
||||
}
|
||||
|
||||
// Read the metadata
|
||||
AString Metadata;
|
||||
Metadata.resize(MetadataLength);
|
||||
if (!m_ReceivedData.ReadBuf((void *)Metadata.data(), MetadataLength))
|
||||
{
|
||||
return PARSE_INCOMPLETE;
|
||||
}
|
||||
|
||||
return ParseItemMetadata(a_Item, Metadata);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cProtocol132::ParseItemMetadata(cItem & a_Item, const AString & a_Metadata)
|
||||
{
|
||||
// Uncompress the GZIPped data:
|
||||
AString Uncompressed;
|
||||
if (UncompressStringGZIP(a_Metadata.data(), a_Metadata.size(), Uncompressed) != Z_OK)
|
||||
{
|
||||
AString HexDump;
|
||||
CreateHexDump(HexDump, a_Metadata.data(), a_Metadata.size(), 16);
|
||||
LOG("Cannot unGZIP item metadata:\n%s", HexDump.c_str());
|
||||
return PARSE_ERROR;
|
||||
}
|
||||
|
||||
// Parse into NBT:
|
||||
cParsedNBT NBT(Uncompressed.data(), Uncompressed.size());
|
||||
if (!NBT.IsValid())
|
||||
{
|
||||
AString HexDump;
|
||||
CreateHexDump(HexDump, Uncompressed.data(), Uncompressed.size(), 16);
|
||||
LOG("Cannot parse NBT item metadata:\n%s", HexDump.c_str());
|
||||
return PARSE_ERROR;
|
||||
}
|
||||
|
||||
// Load enchantments from the NBT:
|
||||
for (int tag = NBT.GetFirstChild(NBT.GetRoot()); tag >= 0; tag = NBT.GetNextSibling(tag))
|
||||
{
|
||||
if (
|
||||
(NBT.GetType(tag) == TAG_List) &&
|
||||
(
|
||||
(NBT.GetName(tag) == "ench") ||
|
||||
(NBT.GetName(tag) == "StoredEnchantments")
|
||||
)
|
||||
)
|
||||
{
|
||||
a_Item.m_Enchantments.ParseFromNBT(NBT, tag);
|
||||
}
|
||||
}
|
||||
|
||||
return PARSE_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocol132::SendCompass(const cWorld & a_World)
|
||||
{
|
||||
cCSLock Lock(m_CSPacket);
|
||||
WriteByte(PACKET_COMPASS);
|
||||
WriteInt((int)(a_World.GetSpawnX()));
|
||||
WriteInt((int)(a_World.GetSpawnY()));
|
||||
WriteInt((int)(a_World.GetSpawnZ()));
|
||||
Flush();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocol132::SendEncryptionKeyRequest(void)
|
||||
{
|
||||
cCSLock Lock(m_CSPacket);
|
||||
WriteByte((char)0xfd);
|
||||
WriteString(cRoot::Get()->GetServer()->GetServerID());
|
||||
WriteShort((short)m_ServerPublicKey.size());
|
||||
SendData(m_ServerPublicKey.data(), m_ServerPublicKey.size());
|
||||
WriteShort(4);
|
||||
WriteInt((int)(intptr_t)this); // Using 'this' as the cryptographic nonce, so that we don't have to generate one each time :)
|
||||
Flush();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocol132::HandleEncryptionKeyResponse(const AString & a_EncKey, const AString & a_EncNonce)
|
||||
{
|
||||
// Decrypt EncNonce using privkey
|
||||
RSAES<PKCS1v15>::Decryptor rsaDecryptor(cRoot::Get()->GetServer()->GetPrivateKey());
|
||||
time_t CurTime = time(NULL);
|
||||
CryptoPP::RandomPool rng;
|
||||
rng.Put((const byte *)&CurTime, sizeof(CurTime));
|
||||
byte DecryptedNonce[MAX_ENC_LEN];
|
||||
DecodingResult res = rsaDecryptor.Decrypt(rng, (const byte *)a_EncNonce.data(), a_EncNonce.size(), DecryptedNonce);
|
||||
if (!res.isValidCoding || (res.messageLength != 4))
|
||||
{
|
||||
LOGD("Bad nonce length");
|
||||
m_Client->Kick("Hacked client");
|
||||
return;
|
||||
}
|
||||
if (ntohl(*((int *)DecryptedNonce)) != (unsigned)(uintptr_t)this)
|
||||
{
|
||||
LOGD("Bad nonce value");
|
||||
m_Client->Kick("Hacked client");
|
||||
return;
|
||||
}
|
||||
|
||||
// Decrypt the symmetric encryption key using privkey:
|
||||
byte DecryptedKey[MAX_ENC_LEN];
|
||||
res = rsaDecryptor.Decrypt(rng, (const byte *)a_EncKey.data(), a_EncKey.size(), DecryptedKey);
|
||||
if (!res.isValidCoding || (res.messageLength != 16))
|
||||
{
|
||||
LOGD("Bad key length");
|
||||
m_Client->Kick("Hacked client");
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
// Send encryption key response:
|
||||
cCSLock Lock(m_CSPacket);
|
||||
WriteByte((char)0xfc);
|
||||
WriteShort(0);
|
||||
WriteShort(0);
|
||||
Flush();
|
||||
}
|
||||
|
||||
StartEncryption(DecryptedKey);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocol132::StartEncryption(const byte * a_Key)
|
||||
{
|
||||
m_Encryptor.SetKey(a_Key, 16, MakeParameters(Name::IV(), ConstByteArrayParameter(a_Key, 16))(Name::FeedbackSize(), 1));
|
||||
m_Decryptor.SetKey(a_Key, 16, MakeParameters(Name::IV(), ConstByteArrayParameter(a_Key, 16))(Name::FeedbackSize(), 1));
|
||||
m_IsEncrypted = true;
|
||||
|
||||
// Prepare the m_AuthServerID:
|
||||
CryptoPP::SHA1 Checksum;
|
||||
AString ServerID = cRoot::Get()->GetServer()->GetServerID();
|
||||
Checksum.Update((const byte *)ServerID.c_str(), ServerID.length());
|
||||
Checksum.Update(a_Key, 16);
|
||||
Checksum.Update((const byte *)m_ServerPublicKey.c_str(), m_ServerPublicKey.length());
|
||||
byte Digest[20];
|
||||
Checksum.Final(Digest);
|
||||
DigestToJava(Digest, m_AuthServerID);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
|
||||
// Protocol132.h
|
||||
|
||||
// Interfaces to the cProtocol132 class representing the release 1.3.2 protocol (#39)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Protocol125.h"
|
||||
#include "../../CryptoPP/modes.h"
|
||||
#include "../../CryptoPP/aes.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cProtocol132 :
|
||||
public cProtocol125
|
||||
{
|
||||
typedef cProtocol125 super;
|
||||
public:
|
||||
|
||||
cProtocol132(cClientHandle * a_Client);
|
||||
virtual ~cProtocol132();
|
||||
|
||||
/// Called when client sends some data:
|
||||
virtual void DataReceived(const char * a_Data, int a_Size) override;
|
||||
|
||||
// Sending commands (alphabetically sorted):
|
||||
virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override;
|
||||
virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override;
|
||||
virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
|
||||
virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override;
|
||||
virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override;
|
||||
virtual void SendDestroyEntity (const cEntity & a_Entity) override;
|
||||
virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override;
|
||||
virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override;
|
||||
virtual void SendPlayerSpawn (const cPlayer & a_Player) override;
|
||||
virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; // a_Src coords are Block * 8
|
||||
virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override;
|
||||
virtual void SendSpawnMob (const cMonster & a_Mob) override;
|
||||
virtual void SendTabCompletionResults(const AStringVector & a_Results) override;
|
||||
virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override;
|
||||
virtual void SendWholeInventory (const cWindow & a_Window) override;
|
||||
|
||||
virtual AString GetAuthServerID(void) override;
|
||||
|
||||
/// Handling of the additional packets:
|
||||
virtual int ParsePacket(unsigned char a_PacketType) override;
|
||||
|
||||
// Modified packets:
|
||||
virtual int ParseBlockPlace (void) override;
|
||||
virtual int ParseHandshake (void) override;
|
||||
virtual int ParseLogin (void) override;
|
||||
virtual int ParsePlayerAbilities(void) override;
|
||||
|
||||
// New packets:
|
||||
virtual int ParseClientStatuses (void);
|
||||
virtual int ParseEncryptionKeyResponse(void);
|
||||
virtual int ParseLocaleViewDistance (void);
|
||||
virtual int ParseTabCompletion (void);
|
||||
|
||||
protected:
|
||||
bool m_IsEncrypted;
|
||||
CryptoPP::CFB_Mode<CryptoPP::AES>::Decryption m_Decryptor;
|
||||
CryptoPP::CFB_Mode<CryptoPP::AES>::Encryption m_Encryptor;
|
||||
AString m_DataToSend;
|
||||
|
||||
/// The ServerID used for session authentication; set in StartEncryption(), used in GetAuthServerID()
|
||||
AString m_AuthServerID;
|
||||
|
||||
/// The server's public key, as used by SendEncryptionKeyRequest() and StartEncryption()
|
||||
AString m_ServerPublicKey;
|
||||
|
||||
virtual void SendData(const char * a_Data, int a_Size) override;
|
||||
|
||||
// DEBUG:
|
||||
virtual void Flush(void) override;
|
||||
|
||||
// Items in slots are sent differently
|
||||
virtual void WriteItem(const cItem & a_Item) override;
|
||||
virtual int ParseItem(cItem & a_Item) override;
|
||||
|
||||
/// Parses the metadata that may come with the item.
|
||||
int ParseItemMetadata(cItem & a_Item, const AString & a_Metadata);
|
||||
|
||||
virtual void SendCompass(const cWorld & a_World);
|
||||
virtual void SendEncryptionKeyRequest(void);
|
||||
|
||||
/// Decrypts the key and nonce, checks nonce, starts the symmetric encryption
|
||||
void HandleEncryptionKeyResponse(const AString & a_EncKey, const AString & a_EncNonce);
|
||||
|
||||
/// Starts the symmetric encryption with the specified key; also sets m_AuthServerID
|
||||
void StartEncryption(const byte * a_Key);
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,256 @@
|
||||
|
||||
// Protocol14x.cpp
|
||||
|
||||
/*
|
||||
Implements the 1.4.x protocol classes representing these protocols:
|
||||
- cProtocol142:
|
||||
- release 1.4.2 protocol (#47)
|
||||
- release 1.4.4 protocol (#49) - the same protocol class is used, because the only difference is in a packet that MCServer doesn't implement yet (ITEM_DATA)
|
||||
- release 1.4.5 protocol (same as 1.4.4)
|
||||
- cProtocol146:
|
||||
- release 1.4.6 protocol (#51)
|
||||
*/
|
||||
|
||||
#include "Globals.h"
|
||||
#include "Protocol14x.h"
|
||||
#include "../Root.h"
|
||||
#include "../Server.h"
|
||||
#include "../ClientHandle.h"
|
||||
#include "../../CryptoPP/randpool.h"
|
||||
#include "../Item.h"
|
||||
#include "ChunkDataSerializer.h"
|
||||
#include "../Entities/Player.h"
|
||||
#include "../Mobs/Monster.h"
|
||||
#include "../UI/Window.h"
|
||||
#include "../Entities/Pickup.h"
|
||||
#include "../Entities/FallingBlock.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#define HANDLE_PACKET_READ(Proc, Type, Var) \
|
||||
Type Var; \
|
||||
{ \
|
||||
if (!m_ReceivedData.Proc(Var)) \
|
||||
{ \
|
||||
m_ReceivedData.CheckValid(); \
|
||||
return PARSE_INCOMPLETE; \
|
||||
} \
|
||||
m_ReceivedData.CheckValid(); \
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
PACKET_UPDATE_TIME = 0x04,
|
||||
PACKET_PICKUP_SPAWN = 0x15,
|
||||
PACKET_SPAWN_OBJECT = 0x17,
|
||||
PACKET_ENTITY_METADATA = 0x28,
|
||||
PACKET_SOUND_PARTICLE_EFFECT = 0x3d
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cProtocol142:
|
||||
|
||||
cProtocol142::cProtocol142(cClientHandle * a_Client) :
|
||||
super(a_Client)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cProtocol142::ParseLocaleViewDistance(void)
|
||||
{
|
||||
HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Locale);
|
||||
HANDLE_PACKET_READ(ReadChar, char, ViewDistance);
|
||||
HANDLE_PACKET_READ(ReadChar, char, ChatFlags);
|
||||
HANDLE_PACKET_READ(ReadChar, char, ClientDifficulty);
|
||||
HANDLE_PACKET_READ(ReadChar, char, ShouldShowCape); // <-- new in 1.4.2
|
||||
// TODO: m_Client->HandleLocale(Locale);
|
||||
// TODO: m_Client->HandleViewDistance(ViewDistance);
|
||||
// TODO: m_Client->HandleChatFlags(ChatFlags);
|
||||
// Ignoring client difficulty
|
||||
return PARSE_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocol142::SendPickupSpawn(const cPickup & a_Pickup)
|
||||
{
|
||||
cCSLock Lock(m_CSPacket);
|
||||
WriteByte (PACKET_PICKUP_SPAWN);
|
||||
WriteInt (a_Pickup.GetUniqueID());
|
||||
WriteItem (a_Pickup.GetItem());
|
||||
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 cProtocol142::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data)
|
||||
{
|
||||
cCSLock Lock(m_CSPacket);
|
||||
WriteByte(PACKET_SOUND_PARTICLE_EFFECT);
|
||||
WriteInt (a_EffectID);
|
||||
WriteInt (a_SrcX);
|
||||
WriteByte(a_SrcY);
|
||||
WriteInt (a_SrcZ);
|
||||
WriteInt (a_Data);
|
||||
WriteBool(0);
|
||||
Flush();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocol142::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay)
|
||||
{
|
||||
cCSLock Lock(m_CSPacket);
|
||||
WriteByte (PACKET_UPDATE_TIME);
|
||||
WriteInt64(a_WorldAge);
|
||||
WriteInt64(a_TimeOfDay);
|
||||
Flush();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cProtocol146:
|
||||
|
||||
cProtocol146::cProtocol146(cClientHandle * a_Client) :
|
||||
super(a_Client)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocol146::SendPickupSpawn(const cPickup & a_Pickup)
|
||||
{
|
||||
ASSERT(!a_Pickup.GetItem().IsEmpty());
|
||||
|
||||
cCSLock Lock(m_CSPacket);
|
||||
|
||||
// Send a SPAWN_OBJECT packet for the base entity:
|
||||
WriteByte(PACKET_SPAWN_OBJECT);
|
||||
WriteInt (a_Pickup.GetUniqueID());
|
||||
WriteByte(0x02);
|
||||
WriteInt ((int)(a_Pickup.GetPosX() * 32));
|
||||
WriteInt ((int)(a_Pickup.GetPosY() * 32));
|
||||
WriteInt ((int)(a_Pickup.GetPosZ() * 32));
|
||||
WriteInt (1);
|
||||
WriteShort((short)(a_Pickup.GetSpeed().x * 32));
|
||||
WriteShort((short)(a_Pickup.GetSpeed().y * 32));
|
||||
WriteShort((short)(a_Pickup.GetSpeed().z * 32));
|
||||
WriteByte(0);
|
||||
WriteByte(0);
|
||||
|
||||
// Send a ENTITY_METADATA packet with the slot info:
|
||||
WriteByte(PACKET_ENTITY_METADATA);
|
||||
WriteInt(a_Pickup.GetUniqueID());
|
||||
WriteByte(0xaa); // a slot value at index 10
|
||||
WriteItem(a_Pickup.GetItem());
|
||||
WriteByte(0x7f); // End of metadata
|
||||
Flush();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocol146::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock)
|
||||
{
|
||||
// Send a spawn object / vehicle packet
|
||||
cCSLock Lock(m_CSPacket);
|
||||
|
||||
WriteByte(PACKET_SPAWN_OBJECT);
|
||||
WriteInt (a_FallingBlock.GetUniqueID());
|
||||
WriteByte(70);
|
||||
WriteInt ((int)(a_FallingBlock.GetPosX() * 32));
|
||||
WriteInt ((int)(a_FallingBlock.GetPosY() * 32));
|
||||
WriteInt ((int)(a_FallingBlock.GetPosZ() * 32));
|
||||
WriteByte (0); // Pitch
|
||||
WriteByte (0); // Yaw
|
||||
WriteInt (a_FallingBlock.GetBlockType()); // data indicator = blocktype
|
||||
WriteShort((short)(a_FallingBlock.GetSpeedX() * 400));
|
||||
WriteShort((short)(a_FallingBlock.GetSpeedY() * 400));
|
||||
WriteShort((short)(a_FallingBlock.GetSpeedZ() * 400));
|
||||
Flush();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocol146::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte 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 cProtocol146::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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
|
||||
// Protocol14x.h
|
||||
|
||||
/*
|
||||
Interfaces to the 1.4.x protocol classes representing these protocols:
|
||||
- cProtocol142:
|
||||
- release 1.4.2 protocol (#47)
|
||||
- release 1.4.4 protocol (#49) - the same protocol class is used, because the only difference is in a packet that MCServer doesn't implement yet (ITEM_DATA)
|
||||
- release 1.4.5 protocol (same as 1.4.4)
|
||||
- cProtocol146:
|
||||
- release 1.4.6 protocol (#51)
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Protocol132.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cProtocol142 :
|
||||
public cProtocol132
|
||||
{
|
||||
typedef cProtocol132 super;
|
||||
|
||||
public:
|
||||
cProtocol142(cClientHandle * a_Client);
|
||||
|
||||
// Sending commands (alphabetically sorted):
|
||||
virtual void SendPickupSpawn (const cPickup & a_Pickup) override;
|
||||
virtual void SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override;
|
||||
virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) override;
|
||||
|
||||
// Specific packet parsers:
|
||||
virtual int ParseLocaleViewDistance(void) override;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cProtocol146 :
|
||||
public cProtocol142
|
||||
{
|
||||
typedef cProtocol142 super;
|
||||
|
||||
public:
|
||||
cProtocol146(cClientHandle * a_Client);
|
||||
|
||||
virtual void SendPickupSpawn (const cPickup & a_Pickup) override;
|
||||
virtual void SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock) override;
|
||||
virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) override;
|
||||
virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) override;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
|
||||
// Protocol15x.cpp
|
||||
|
||||
/*
|
||||
Implements the 1.5.x protocol classes:
|
||||
- cProtocol150
|
||||
- release 1.5 protocol (#60)
|
||||
- release 1.5.2 protocol (#61, no relevant changes found)
|
||||
*/
|
||||
|
||||
#include "Globals.h"
|
||||
#include "Protocol15x.h"
|
||||
#include "../ClientHandle.h"
|
||||
#include "../Item.h"
|
||||
#include "../UI/Window.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#define HANDLE_PACKET_READ(Proc, Type, Var) \
|
||||
Type Var; \
|
||||
{ \
|
||||
if (!m_ReceivedData.Proc(Var)) \
|
||||
{ \
|
||||
m_ReceivedData.CheckValid(); \
|
||||
return PARSE_INCOMPLETE; \
|
||||
} \
|
||||
m_ReceivedData.CheckValid(); \
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
PACKET_WINDOW_OPEN = 0x64,
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cProtocol150:
|
||||
|
||||
cProtocol150::cProtocol150(cClientHandle * a_Client) :
|
||||
super(a_Client)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocol150::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());
|
||||
WriteByte (1); // Use title
|
||||
Flush();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cProtocol150::ParseWindowClick(void)
|
||||
{
|
||||
HANDLE_PACKET_READ(ReadChar, char, WindowID);
|
||||
HANDLE_PACKET_READ(ReadBEShort, short, SlotNum);
|
||||
HANDLE_PACKET_READ(ReadByte, Byte, Button);
|
||||
HANDLE_PACKET_READ(ReadBEShort, short, TransactionID);
|
||||
HANDLE_PACKET_READ(ReadByte, Byte, Mode);
|
||||
cItem HeldItem;
|
||||
int res = ParseItem(HeldItem);
|
||||
if (res < 0)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
// Convert Button, Mode, SlotNum and HeldItem into eClickAction:
|
||||
eClickAction Action;
|
||||
switch ((Mode << 8) | Button)
|
||||
{
|
||||
case 0x0000: Action = (SlotNum != -999) ? caLeftClick : caLeftClickOutside; break;
|
||||
case 0x0001: Action = (SlotNum != -999) ? caRightClick : caRightClickOutside; break;
|
||||
case 0x0100: Action = caShiftLeftClick; break;
|
||||
case 0x0101: Action = caShiftRightClick; break;
|
||||
case 0x0200: Action = caNumber1; break;
|
||||
case 0x0201: Action = caNumber2; break;
|
||||
case 0x0202: Action = caNumber3; break;
|
||||
case 0x0203: Action = caNumber4; break;
|
||||
case 0x0204: Action = caNumber5; break;
|
||||
case 0x0205: Action = caNumber6; break;
|
||||
case 0x0206: Action = caNumber7; break;
|
||||
case 0x0207: Action = caNumber8; break;
|
||||
case 0x0208: Action = caNumber9; break;
|
||||
case 0x0300: Action = caMiddleClick; break;
|
||||
case 0x0400: Action = (SlotNum == -999) ? caLeftClickOutsideHoldNothing : caDropKey; break;
|
||||
case 0x0401: Action = (SlotNum == -999) ? caRightClickOutsideHoldNothing : caCtrlDropKey; break;
|
||||
case 0x0500: Action = (SlotNum == -999) ? caLeftPaintBegin : caUnknown; break;
|
||||
case 0x0501: Action = (SlotNum != -999) ? caLeftPaintProgress : caUnknown; break;
|
||||
case 0x0502: Action = (SlotNum == -999) ? caLeftPaintEnd : caUnknown; break;
|
||||
case 0x0504: Action = (SlotNum == -999) ? caRightPaintBegin : caUnknown; break;
|
||||
case 0x0505: Action = (SlotNum != -999) ? caRightPaintProgress : caUnknown; break;
|
||||
case 0x0506: Action = (SlotNum == -999) ? caRightPaintEnd : caUnknown; break;
|
||||
case 0x0600: Action = caDblClick; break;
|
||||
}
|
||||
|
||||
if (Action == caUnknown)
|
||||
{
|
||||
LOGWARNING("Received an unknown click action combination: Mode = %d, Button = %d, Slot = %d, HeldItem = %s. Ignoring packet.",
|
||||
Mode, Button, SlotNum, ItemToFullString(HeldItem).c_str()
|
||||
);
|
||||
ASSERT(!"Unknown click action");
|
||||
return PARSE_OK;
|
||||
}
|
||||
|
||||
m_Client->HandleWindowClick(WindowID, SlotNum, Action, HeldItem);
|
||||
return PARSE_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
|
||||
// Protocol15x.h
|
||||
|
||||
/*
|
||||
Declares the 1.5.x protocol classes:
|
||||
- cProtocol150
|
||||
- release 1.5 and 1.5.1 protocol (#60)
|
||||
- release 1.5.2 protocol (#61; no relevant changes found)
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Protocol14x.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cProtocol150 :
|
||||
public cProtocol146
|
||||
{
|
||||
typedef cProtocol146 super;
|
||||
|
||||
public:
|
||||
cProtocol150(cClientHandle * a_Client);
|
||||
|
||||
virtual void SendWindowOpen(const cWindow & a_Window) override;
|
||||
|
||||
virtual int ParseWindowClick(void);
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,268 @@
|
||||
|
||||
// Protocol16x.cpp
|
||||
|
||||
/*
|
||||
Implements the 1.6.x protocol classes:
|
||||
- cProtocol161
|
||||
- release 1.6.1 protocol (#73)
|
||||
- cProtocol162
|
||||
- release 1.6.2 protocol (#74)
|
||||
- release 1.6.3 protocol (#77) - no relevant changes
|
||||
- release 1.6.4 protocol (#78) - no relevant changes
|
||||
(others may be added later in the future for the 1.6 release series)
|
||||
*/
|
||||
|
||||
#include "Globals.h"
|
||||
#include "Protocol16x.h"
|
||||
#include "../ClientHandle.h"
|
||||
#include "../Entities/Entity.h"
|
||||
#include "../Entities/Player.h"
|
||||
#include "../UI/Window.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#define HANDLE_PACKET_READ(Proc, Type, Var) \
|
||||
Type Var; \
|
||||
{ \
|
||||
if (!m_ReceivedData.Proc(Var)) \
|
||||
{ \
|
||||
m_ReceivedData.CheckValid(); \
|
||||
return PARSE_INCOMPLETE; \
|
||||
} \
|
||||
m_ReceivedData.CheckValid(); \
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
PACKET_CHAT = 0x03,
|
||||
PACKET_UPDATE_HEALTH = 0x08,
|
||||
PACKET_STEER_VEHICLE = 0x1b,
|
||||
PACKET_ATTACH_ENTITY = 0x27,
|
||||
PACKET_ENTITY_PROPERTIES = 0x2c,
|
||||
PACKET_WINDOW_OPEN = 0x64,
|
||||
PACKET_TILE_EDITOR_OPEN = 0x85,
|
||||
PACKET_PLAYER_ABILITIES = 0xca,
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cProtocol161:
|
||||
|
||||
cProtocol161::cProtocol161(cClientHandle * a_Client) :
|
||||
super(a_Client)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocol161::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());
|
||||
WriteBool(false); // TODO: "Should use leash?" -> no
|
||||
Flush();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocol161::SendChat(const AString & a_Message)
|
||||
{
|
||||
super::SendChat(Printf("{\"text\":\"%s\"}", EscapeString(a_Message).c_str()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocol161::SendEditSign(int a_BlockX, int a_BlockY, int a_BlockZ)
|
||||
{
|
||||
cCSLock Lock(m_CSPacket);
|
||||
WriteByte(PACKET_TILE_EDITOR_OPEN);
|
||||
WriteByte(0);
|
||||
WriteInt(a_BlockX);
|
||||
WriteInt(a_BlockY);
|
||||
WriteInt(a_BlockZ);
|
||||
Flush();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocol161::SendGameMode(eGameMode a_GameMode)
|
||||
{
|
||||
super::SendGameMode(a_GameMode);
|
||||
SendPlayerMaxSpeed();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocol161::SendHealth(void)
|
||||
{
|
||||
cCSLock Lock(m_CSPacket);
|
||||
WriteByte (PACKET_UPDATE_HEALTH);
|
||||
WriteFloat((float)m_Client->GetPlayer()->GetHealth());
|
||||
WriteShort(m_Client->GetPlayer()->GetFoodLevel());
|
||||
WriteFloat((float)m_Client->GetPlayer()->GetFoodSaturationLevel());
|
||||
Flush();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocol161::SendPlayerMaxSpeed(void)
|
||||
{
|
||||
cCSLock Lock(m_CSPacket);
|
||||
WriteByte(PACKET_ENTITY_PROPERTIES);
|
||||
WriteInt(m_Client->GetPlayer()->GetUniqueID());
|
||||
WriteInt(1);
|
||||
WriteString("generic.movementSpeed");
|
||||
WriteDouble(m_Client->GetPlayer()->GetMaxSpeed());
|
||||
Flush();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocol161::SendRespawn(void)
|
||||
{
|
||||
// Besides sending the respawn, we need to also send the player max speed, otherwise the client reverts to super-fast
|
||||
super::SendRespawn();
|
||||
SendPlayerMaxSpeed();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocol161::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());
|
||||
WriteByte (1); // Use title
|
||||
if (a_Window.GetWindowType() == cWindow::wtAnimalChest)
|
||||
{
|
||||
WriteInt(0); // TODO: The animal's EntityID
|
||||
}
|
||||
Flush();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cProtocol161::ParseEntityAction(void)
|
||||
{
|
||||
HANDLE_PACKET_READ(ReadBEInt, int, EntityID);
|
||||
HANDLE_PACKET_READ(ReadChar, char, ActionID);
|
||||
HANDLE_PACKET_READ(ReadBEInt, int, UnknownHorseVal);
|
||||
m_Client->HandleEntityAction(EntityID, ActionID);
|
||||
return PARSE_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cProtocol161::ParsePlayerAbilities(void)
|
||||
{
|
||||
HANDLE_PACKET_READ(ReadByte, Byte, Flags);
|
||||
HANDLE_PACKET_READ(ReadBEFloat, float, FlyingSpeed);
|
||||
HANDLE_PACKET_READ(ReadBEFloat, float, WalkingSpeed);
|
||||
// TODO: m_Client->HandlePlayerAbilities(...);
|
||||
return PARSE_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cProtocol161::ParseSteerVehicle(void)
|
||||
{
|
||||
HANDLE_PACKET_READ(ReadBEFloat, float, Sideways);
|
||||
HANDLE_PACKET_READ(ReadBEFloat, float, Forward);
|
||||
HANDLE_PACKET_READ(ReadBool, bool, Jump);
|
||||
HANDLE_PACKET_READ(ReadBool, bool, Unmount);
|
||||
if (Unmount)
|
||||
{
|
||||
m_Client->HandleUnmount();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Client->HandleSteerVehicle(Forward, Sideways);
|
||||
}
|
||||
return PARSE_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cProtocol161::ParsePacket(unsigned char a_PacketType)
|
||||
{
|
||||
switch (a_PacketType)
|
||||
{
|
||||
case PACKET_STEER_VEHICLE: return ParseSteerVehicle();
|
||||
default: return super::ParsePacket(a_PacketType);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cProtocol162:
|
||||
|
||||
cProtocol162::cProtocol162(cClientHandle * a_Client) :
|
||||
super(a_Client)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocol162::SendPlayerMaxSpeed(void)
|
||||
{
|
||||
cCSLock Lock(m_CSPacket);
|
||||
WriteByte(PACKET_ENTITY_PROPERTIES);
|
||||
WriteInt(m_Client->GetPlayer()->GetUniqueID());
|
||||
WriteInt(1);
|
||||
WriteString("generic.movementSpeed");
|
||||
WriteDouble(m_Client->GetPlayer()->GetMaxSpeed());
|
||||
WriteShort(0);
|
||||
Flush();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
|
||||
// Protocol16x.h
|
||||
|
||||
/*
|
||||
Declares the 1.6.x protocol classes:
|
||||
- cProtocol161
|
||||
- release 1.6.1 protocol (#73)
|
||||
- cProtocol162
|
||||
- release 1.6.2 protocol (#74)
|
||||
- release 1.6.3 protocol (#77) - no relevant changes
|
||||
- release 1.6.4 protocol (#78) - no relevant changes
|
||||
(others may be added later in the future for the 1.6 release series)
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Protocol15x.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cProtocol161 :
|
||||
public cProtocol150
|
||||
{
|
||||
typedef cProtocol150 super;
|
||||
|
||||
public:
|
||||
cProtocol161(cClientHandle * a_Client);
|
||||
|
||||
protected:
|
||||
|
||||
// cProtocol150 overrides:
|
||||
virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override;
|
||||
virtual void SendChat (const AString & a_Message) override;
|
||||
virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) override; ///< Request the client to open up the sign editor for the sign (1.6+)
|
||||
virtual void SendGameMode (eGameMode a_GameMode) override;
|
||||
virtual void SendHealth (void) override;
|
||||
virtual void SendPlayerMaxSpeed(void) override;
|
||||
virtual void SendRespawn (void) override;
|
||||
virtual void SendWindowOpen (const cWindow & a_Window) override;
|
||||
|
||||
virtual int ParseEntityAction (void) override;
|
||||
virtual int ParsePlayerAbilities(void) override;
|
||||
|
||||
// New packets:
|
||||
virtual int ParseSteerVehicle(void);
|
||||
|
||||
// Enable new packets' handling
|
||||
virtual int ParsePacket(unsigned char a_PacketType) override;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cProtocol162 :
|
||||
public cProtocol161
|
||||
{
|
||||
typedef cProtocol161 super;
|
||||
|
||||
public:
|
||||
cProtocol162(cClientHandle * a_Client);
|
||||
|
||||
protected:
|
||||
// cProtocol161 overrides:
|
||||
virtual void SendPlayerMaxSpeed(void) override;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,258 @@
|
||||
|
||||
// Protocol17x.h
|
||||
|
||||
/*
|
||||
Declares the 1.7.x protocol classes:
|
||||
- cProtocol172
|
||||
- release 1.7.2 protocol (#4)
|
||||
(others may be added later in the future for the 1.7 release series)
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Protocol.h"
|
||||
#include "../ByteBuffer.h"
|
||||
#include "../../CryptoPP/modes.h"
|
||||
#include "../../CryptoPP/aes.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cProtocol172 :
|
||||
public cProtocol // TODO
|
||||
{
|
||||
typedef cProtocol super; // TODO
|
||||
|
||||
public:
|
||||
|
||||
cProtocol172(cClientHandle * a_Client, const AString & a_ServerAddress, UInt16 a_ServerPort, UInt32 a_State);
|
||||
|
||||
/// Called when client sends some data:
|
||||
virtual void DataReceived(const char * a_Data, int a_Size) override;
|
||||
|
||||
/// Sending stuff to clients (alphabetically sorted):
|
||||
virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override;
|
||||
virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override;
|
||||
virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override;
|
||||
virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
|
||||
virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override;
|
||||
virtual void SendChat (const AString & a_Message) override;
|
||||
virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override;
|
||||
virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override;
|
||||
virtual void SendDestroyEntity (const cEntity & a_Entity) override;
|
||||
virtual void SendDisconnect (const AString & a_Reason) override;
|
||||
virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) override; ///< Request the client to open up the sign editor for the sign (1.6+)
|
||||
virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override;
|
||||
virtual void SendEntityHeadLook (const cEntity & a_Entity) override;
|
||||
virtual void SendEntityLook (const cEntity & a_Entity) override;
|
||||
virtual void SendEntityMetadata (const cEntity & a_Entity) override;
|
||||
virtual void SendEntityProperties (const cEntity & a_Entity) override;
|
||||
virtual void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override;
|
||||
virtual void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override;
|
||||
virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) override;
|
||||
virtual void SendEntityVelocity (const cEntity & a_Entity) override;
|
||||
virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) override;
|
||||
virtual void SendGameMode (eGameMode a_GameMode) override;
|
||||
virtual void SendHealth (void) override;
|
||||
virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override;
|
||||
virtual void SendKeepAlive (int a_PingID) override;
|
||||
virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override;
|
||||
virtual void SendPickupSpawn (const cPickup & a_Pickup) override;
|
||||
virtual void SendPlayerAbilities (void) override;
|
||||
virtual void SendPlayerAnimation (const cPlayer & a_Player, char a_Animation) override;
|
||||
virtual void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline) override;
|
||||
virtual void SendPlayerMaxSpeed (void) override;
|
||||
virtual void SendPlayerMoveLook (void) override;
|
||||
virtual void SendPlayerPosition (void) override;
|
||||
virtual void SendPlayerSpawn (const cPlayer & a_Player) override;
|
||||
virtual void SendRespawn (void) override;
|
||||
virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; // a_Src coords are Block * 8
|
||||
virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override;
|
||||
virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override;
|
||||
virtual void SendSpawnMob (const cMonster & a_Mob) override;
|
||||
virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) override;
|
||||
virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) override;
|
||||
virtual void SendTabCompletionResults(const AStringVector & a_Results) override;
|
||||
virtual void SendTeleportEntity (const cEntity & a_Entity) override;
|
||||
virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override;
|
||||
virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) override;
|
||||
virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override;
|
||||
virtual void 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) override;
|
||||
virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) override;
|
||||
virtual void SendWeather (eWeather a_Weather) override;
|
||||
virtual void SendWholeInventory (const cWindow & a_Window) override;
|
||||
virtual void SendWindowClose (const cWindow & a_Window) override;
|
||||
virtual void SendWindowOpen (const cWindow & a_Window) override;
|
||||
virtual void SendWindowProperty (const cWindow & a_Window, short a_Property, short a_Value) override;
|
||||
|
||||
virtual AString GetAuthServerID(void) override { return m_AuthServerID; }
|
||||
|
||||
protected:
|
||||
|
||||
/// Composes individual packets in the protocol's m_OutPacketBuffer; sends them upon being destructed
|
||||
class cPacketizer
|
||||
{
|
||||
public:
|
||||
cPacketizer(cProtocol172 & a_Protocol, UInt32 a_PacketType) :
|
||||
m_Protocol(a_Protocol),
|
||||
m_Out(a_Protocol.m_OutPacketBuffer),
|
||||
m_Lock(a_Protocol.m_CSPacket)
|
||||
{
|
||||
m_Out.WriteVarInt(a_PacketType);
|
||||
}
|
||||
|
||||
~cPacketizer();
|
||||
|
||||
void WriteBool(bool a_Value)
|
||||
{
|
||||
m_Out.WriteBool(a_Value);
|
||||
}
|
||||
|
||||
void WriteByte(Byte a_Value)
|
||||
{
|
||||
m_Out.WriteByte(a_Value);
|
||||
}
|
||||
|
||||
void WriteChar(char a_Value)
|
||||
{
|
||||
m_Out.WriteChar(a_Value);
|
||||
}
|
||||
|
||||
void WriteShort(short a_Value)
|
||||
{
|
||||
m_Out.WriteBEShort(a_Value);
|
||||
}
|
||||
|
||||
void WriteInt(int a_Value)
|
||||
{
|
||||
m_Out.WriteBEInt(a_Value);
|
||||
}
|
||||
|
||||
void WriteInt64(Int64 a_Value)
|
||||
{
|
||||
m_Out.WriteBEInt64(a_Value);
|
||||
}
|
||||
|
||||
void WriteFloat(float a_Value)
|
||||
{
|
||||
m_Out.WriteBEFloat(a_Value);
|
||||
}
|
||||
|
||||
void WriteDouble(double a_Value)
|
||||
{
|
||||
m_Out.WriteBEDouble(a_Value);
|
||||
}
|
||||
|
||||
void WriteVarInt(UInt32 a_Value)
|
||||
{
|
||||
m_Out.WriteVarInt(a_Value);
|
||||
}
|
||||
|
||||
void WriteString(const AString & a_Value)
|
||||
{
|
||||
m_Out.WriteVarUTF8String(a_Value);
|
||||
}
|
||||
|
||||
void WriteBuf(const char * a_Data, int a_Size)
|
||||
{
|
||||
m_Out.Write(a_Data, a_Size);
|
||||
}
|
||||
|
||||
void WriteItem(const cItem & a_Item);
|
||||
void WriteByteAngle(double a_Angle); // Writes the specified angle using a single byte
|
||||
void WriteFPInt(double a_Value); // Writes the double value as a 27:5 fixed-point integer
|
||||
void WriteEntityMetadata(const cEntity & a_Entity); // Writes the metadata for the specified entity, not including the terminating 0x7f
|
||||
void WriteMobMetadata(const cMonster & a_Mob); // Writes the mob-specific metadata for the specified mob
|
||||
void WriteEntityProperties(const cEntity & a_Entity); // Writes the entity properties for the specified entity, including the Count field
|
||||
|
||||
protected:
|
||||
cProtocol172 & m_Protocol;
|
||||
cByteBuffer & m_Out;
|
||||
cCSLock m_Lock;
|
||||
} ;
|
||||
|
||||
AString m_ServerAddress;
|
||||
|
||||
UInt16 m_ServerPort;
|
||||
|
||||
AString m_AuthServerID;
|
||||
|
||||
/// State of the protocol. 1 = status, 2 = login, 3 = game
|
||||
UInt32 m_State;
|
||||
|
||||
/// Buffer for the received data
|
||||
cByteBuffer m_ReceivedData;
|
||||
|
||||
/// Buffer for composing the outgoing packets, through cPacketizer
|
||||
cByteBuffer m_OutPacketBuffer;
|
||||
|
||||
/// Buffer for composing packet length (so that each cPacketizer instance doesn't allocate a new cPacketBuffer)
|
||||
cByteBuffer m_OutPacketLenBuffer;
|
||||
|
||||
bool m_IsEncrypted;
|
||||
CryptoPP::CFB_Mode<CryptoPP::AES>::Decryption m_Decryptor;
|
||||
CryptoPP::CFB_Mode<CryptoPP::AES>::Encryption m_Encryptor;
|
||||
|
||||
|
||||
/// Adds the received (unencrypted) data to m_ReceivedData, parses complete packets
|
||||
void AddReceivedData(const char * a_Data, int a_Size);
|
||||
|
||||
/// Reads and handles the packet. The packet length and type have already been read.
|
||||
void HandlePacket(UInt32 a_PacketType, UInt32 a_RemainingBytes);
|
||||
|
||||
// Packet handlers while in the Status state (m_State == 1):
|
||||
void HandlePacketStatusPing (UInt32 a_RemainingBytes);
|
||||
void HandlePacketStatusRequest(UInt32 a_RemainingBytes);
|
||||
|
||||
// Packet handlers while in the Login state (m_State == 2):
|
||||
void HandlePacketLoginEncryptionResponse(UInt32 a_RemainingBytes);
|
||||
void HandlePacketLoginStart (UInt32 a_RemainingBytes);
|
||||
|
||||
// Packet handlers while in the Game state (m_State == 3):
|
||||
void HandlePacketAnimation (UInt32 a_RemainingBytes);
|
||||
void HandlePacketBlockDig (UInt32 a_RemainingBytes);
|
||||
void HandlePacketBlockPlace (UInt32 a_RemainingBytes);
|
||||
void HandlePacketChatMessage (UInt32 a_RemainingBytes);
|
||||
void HandlePacketClientSettings (UInt32 a_RemainingBytes);
|
||||
void HandlePacketClientStatus (UInt32 a_RemainingBytes);
|
||||
void HandlePacketCreativeInventoryAction(UInt32 a_RemainingBytes);
|
||||
void HandlePacketEntityAction (UInt32 a_RemainingBytes);
|
||||
void HandlePacketKeepAlive (UInt32 a_RemainingBytes);
|
||||
void HandlePacketPlayer (UInt32 a_RemainingBytes);
|
||||
void HandlePacketPlayerAbilities (UInt32 a_RemainingBytes);
|
||||
void HandlePacketPlayerLook (UInt32 a_RemainingBytes);
|
||||
void HandlePacketPlayerPos (UInt32 a_RemainingBytes);
|
||||
void HandlePacketPlayerPosLook (UInt32 a_RemainingBytes);
|
||||
void HandlePacketPluginMessage (UInt32 a_RemainingBytes);
|
||||
void HandlePacketSlotSelect (UInt32 a_RemainingBytes);
|
||||
void HandlePacketSteerVehicle (UInt32 a_RemainingBytes);
|
||||
void HandlePacketTabComplete (UInt32 a_RemainingBytes);
|
||||
void HandlePacketUpdateSign (UInt32 a_RemainingBytes);
|
||||
void HandlePacketUseEntity (UInt32 a_RemainingBytes);
|
||||
void HandlePacketWindowClick (UInt32 a_RemainingBytes);
|
||||
void HandlePacketWindowClose (UInt32 a_RemainingBytes);
|
||||
|
||||
|
||||
/// Writes an entire packet into the output stream. a_Packet is expected to start with the packet type; data length is prepended here.
|
||||
void WritePacket(cByteBuffer & a_Packet);
|
||||
|
||||
/// Sends the data to the client, encrypting them if needed.
|
||||
virtual void SendData(const char * a_Data, int a_Size) override;
|
||||
|
||||
void SendCompass(const cWorld & a_World);
|
||||
|
||||
/// Reads an item out of the received data, sets a_Item to the values read. Returns false if not enough received data
|
||||
bool ReadItem(cItem & a_Item);
|
||||
|
||||
/// Parses item metadata as read by ReadItem(), into the item enchantments.
|
||||
void ParseItemMetadata(cItem & a_Item, const AString & a_Metadata);
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,905 @@
|
||||
|
||||
// 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 "Protocol15x.h"
|
||||
#include "Protocol16x.h"
|
||||
#include "Protocol17x.h"
|
||||
#include "../ClientHandle.h"
|
||||
#include "../Root.h"
|
||||
#include "../Server.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";
|
||||
case PROTO_VERSION_1_5_0: return "1.5";
|
||||
case PROTO_VERSION_1_5_2: return "1.5.2";
|
||||
case PROTO_VERSION_1_6_1: return "1.6.1";
|
||||
case PROTO_VERSION_1_6_2: return "1.6.2";
|
||||
case PROTO_VERSION_1_6_3: return "1.6.3";
|
||||
case PROTO_VERSION_1_6_4: return "1.6.4";
|
||||
case PROTO_VERSION_1_7_2: return "1.7.2";
|
||||
}
|
||||
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::SendAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle)
|
||||
{
|
||||
ASSERT(m_Protocol != NULL);
|
||||
m_Protocol->SendAttachEntity(a_Entity, a_Vehicle);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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::SendEditSign(int a_BlockX, int a_BlockY, int a_BlockZ)
|
||||
{
|
||||
ASSERT(m_Protocol != NULL);
|
||||
m_Protocol->SendEditSign(a_BlockX, a_BlockY, a_BlockZ);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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::SendEntityHeadLook(const cEntity & a_Entity)
|
||||
{
|
||||
ASSERT(m_Protocol != NULL);
|
||||
m_Protocol->SendEntityHeadLook(a_Entity);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocolRecognizer::SendEntityLook(const cEntity & a_Entity)
|
||||
{
|
||||
ASSERT(m_Protocol != NULL);
|
||||
m_Protocol->SendEntityLook(a_Entity);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocolRecognizer::SendEntityMetadata(const cEntity & a_Entity)
|
||||
{
|
||||
ASSERT(m_Protocol != NULL);
|
||||
m_Protocol->SendEntityMetadata(a_Entity);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocolRecognizer::SendEntityProperties(const cEntity & a_Entity)
|
||||
{
|
||||
ASSERT(m_Protocol != NULL);
|
||||
m_Protocol->SendEntityProperties(a_Entity);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocolRecognizer::SendEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ)
|
||||
{
|
||||
ASSERT(m_Protocol != NULL);
|
||||
m_Protocol->SendEntityRelMove(a_Entity, a_RelX, a_RelY, a_RelZ);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocolRecognizer::SendEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ)
|
||||
{
|
||||
ASSERT(m_Protocol != NULL);
|
||||
m_Protocol->SendEntityRelMoveLook(a_Entity, a_RelX, a_RelY, a_RelZ);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocolRecognizer::SendEntityStatus(const cEntity & a_Entity, char a_Status)
|
||||
{
|
||||
ASSERT(m_Protocol != NULL);
|
||||
m_Protocol->SendEntityStatus(a_Entity, a_Status);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocolRecognizer::SendEntityVelocity(const cEntity & a_Entity)
|
||||
{
|
||||
ASSERT(m_Protocol != NULL);
|
||||
m_Protocol->SendEntityVelocity(a_Entity);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocolRecognizer::SendExplosion(double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion)
|
||||
{
|
||||
ASSERT(m_Protocol != NULL);
|
||||
m_Protocol->SendExplosion(a_BlockX,a_BlockY,a_BlockZ,a_Radius, a_BlocksAffected, a_PlayerMotion);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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::SendWindowProperty(const cWindow & a_Window, short a_Property, short a_Value)
|
||||
{
|
||||
ASSERT(m_Protocol != NULL);
|
||||
m_Protocol->SendWindowProperty(a_Window, a_Property, 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::SendPickupSpawn(const cPickup & a_Pickup)
|
||||
{
|
||||
ASSERT(m_Protocol != NULL);
|
||||
m_Protocol->SendPickupSpawn(a_Pickup);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocolRecognizer::SendPlayerAbilities(void)
|
||||
{
|
||||
ASSERT(m_Protocol != NULL);
|
||||
m_Protocol->SendPlayerAbilities();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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::SendPlayerMaxSpeed(void)
|
||||
{
|
||||
ASSERT(m_Protocol != NULL);
|
||||
m_Protocol->SendPlayerMaxSpeed();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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, Byte a_Yaw, Byte a_Pitch)
|
||||
{
|
||||
ASSERT(m_Protocol != NULL);
|
||||
m_Protocol->SendSpawnObject(a_Entity, a_ObjectType, a_ObjectData, a_Yaw, a_Pitch);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocolRecognizer::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType)
|
||||
{
|
||||
ASSERT(m_Protocol != NULL);
|
||||
m_Protocol->SendSpawnVehicle(a_Vehicle, a_VehicleType, a_VehicleSubType);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocolRecognizer::SendTabCompletionResults(const AStringVector & a_Results)
|
||||
{
|
||||
ASSERT(m_Protocol != NULL);
|
||||
m_Protocol->SendTabCompletionResults(a_Results);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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 cWindow & a_Window)
|
||||
{
|
||||
ASSERT(m_Protocol != NULL);
|
||||
m_Protocol->SendWholeInventory(a_Window);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocolRecognizer::SendWindowClose(const cWindow & a_Window)
|
||||
{
|
||||
ASSERT(m_Protocol != NULL);
|
||||
m_Protocol->SendWindowClose(a_Window);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocolRecognizer::SendWindowOpen(const cWindow & a_Window)
|
||||
{
|
||||
ASSERT(m_Protocol != NULL);
|
||||
m_Protocol->SendWindowOpen(a_Window);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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: return TryRecognizeLengthlessProtocol(); // Handshake, continue recognizing
|
||||
case 0xfe:
|
||||
{
|
||||
// This may be either a packet length or the length-less Ping packet
|
||||
Byte NextByte;
|
||||
if (!m_Buffer.ReadByte(NextByte))
|
||||
{
|
||||
// Not enough data for either protocol
|
||||
// This could actually happen with the 1.2 / 1.3 client, but their support is fading out anyway
|
||||
return false;
|
||||
}
|
||||
if (NextByte != 0x01)
|
||||
{
|
||||
// This is definitely NOT a length-less Ping packet, handle as lengthed protocol:
|
||||
break;
|
||||
}
|
||||
if (!m_Buffer.ReadByte(NextByte))
|
||||
{
|
||||
// There is no more data. Although this *could* mean TCP fragmentation, it is highly unlikely
|
||||
// and rather this is a 1.4 client sending a regular Ping packet (without the following Plugin message)
|
||||
SendLengthlessServerPing();
|
||||
return false;
|
||||
}
|
||||
if (NextByte == 0xfa)
|
||||
{
|
||||
// Definitely a length-less Ping followed by a Plugin message
|
||||
SendLengthlessServerPing();
|
||||
return false;
|
||||
}
|
||||
// Definitely a lengthed Initial handshake, handle below:
|
||||
break;
|
||||
}
|
||||
} // switch (PacketType)
|
||||
|
||||
// This must be a lengthed protocol, try if it has the entire initial handshake packet:
|
||||
m_Buffer.ResetRead();
|
||||
UInt32 PacketLen;
|
||||
UInt32 ReadSoFar = m_Buffer.GetReadableSpace();
|
||||
if (!m_Buffer.ReadVarInt(PacketLen))
|
||||
{
|
||||
// Not enough bytes for the packet length, keep waiting
|
||||
return false;
|
||||
}
|
||||
ReadSoFar -= m_Buffer.GetReadableSpace();
|
||||
if (!m_Buffer.CanReadBytes(PacketLen))
|
||||
{
|
||||
// Not enough bytes for the packet, keep waiting
|
||||
return false;
|
||||
}
|
||||
return TryRecognizeLengthedProtocol(PacketLen - ReadSoFar);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cProtocolRecognizer::TryRecognizeLengthlessProtocol(void)
|
||||
{
|
||||
// The comm started with 0x02, which is a Handshake packet in the length-less protocol family
|
||||
// 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;
|
||||
}
|
||||
case PROTO_VERSION_1_5_0:
|
||||
case PROTO_VERSION_1_5_2:
|
||||
{
|
||||
m_Protocol = new cProtocol150(m_Client);
|
||||
return true;
|
||||
}
|
||||
case PROTO_VERSION_1_6_1:
|
||||
{
|
||||
m_Protocol = new cProtocol161(m_Client);
|
||||
return true;
|
||||
}
|
||||
case PROTO_VERSION_1_6_2:
|
||||
case PROTO_VERSION_1_6_3:
|
||||
case PROTO_VERSION_1_6_4:
|
||||
{
|
||||
m_Protocol = new cProtocol162(m_Client);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
m_Protocol = new cProtocol125(m_Client);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cProtocolRecognizer::TryRecognizeLengthedProtocol(UInt32 a_PacketLengthRemaining)
|
||||
{
|
||||
UInt32 PacketType;
|
||||
UInt32 NumBytesRead = m_Buffer.GetReadableSpace();
|
||||
if (!m_Buffer.ReadVarInt(PacketType))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (PacketType != 0x00)
|
||||
{
|
||||
// Not an initial handshake packet, we don't know how to talk to them
|
||||
LOGINFO("Client \"%s\" uses an unsupported protocol (lengthed, initial packet %u)",
|
||||
m_Client->GetIPString().c_str(), PacketType
|
||||
);
|
||||
m_Client->Kick("Unsupported protocol version");
|
||||
return false;
|
||||
}
|
||||
UInt32 ProtocolVersion;
|
||||
if (!m_Buffer.ReadVarInt(ProtocolVersion))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
NumBytesRead -= m_Buffer.GetReadableSpace();
|
||||
switch (ProtocolVersion)
|
||||
{
|
||||
case PROTO_VERSION_1_7_2:
|
||||
{
|
||||
AString ServerAddress;
|
||||
short ServerPort;
|
||||
UInt32 NextState;
|
||||
m_Buffer.ReadVarUTF8String(ServerAddress);
|
||||
m_Buffer.ReadBEShort(ServerPort);
|
||||
m_Buffer.ReadVarInt(NextState);
|
||||
m_Buffer.CommitRead();
|
||||
m_Protocol = new cProtocol172(m_Client, ServerAddress, ServerPort, NextState);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
LOGINFO("Client \"%s\" uses an unsupported protocol (lengthed, version %u)",
|
||||
m_Client->GetIPString().c_str(), ProtocolVersion
|
||||
);
|
||||
m_Client->Kick("Unsupported protocol version");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocolRecognizer::SendLengthlessServerPing(void)
|
||||
{
|
||||
AString Reply;
|
||||
switch (cRoot::Get()->GetPrimaryServerVersion())
|
||||
{
|
||||
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()->GetServer()->GetDescription().c_str(),
|
||||
cChatColor::Delimiter.c_str(),
|
||||
cRoot::Get()->GetServer()->GetNumPlayers(),
|
||||
cChatColor::Delimiter.c_str(),
|
||||
cRoot::Get()->GetServer()->GetMaxPlayers()
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
case PROTO_VERSION_1_4_2:
|
||||
case PROTO_VERSION_1_4_4:
|
||||
case PROTO_VERSION_1_4_6:
|
||||
case PROTO_VERSION_1_5_0:
|
||||
case PROTO_VERSION_1_5_2:
|
||||
case PROTO_VERSION_1_6_1:
|
||||
case PROTO_VERSION_1_6_2:
|
||||
case PROTO_VERSION_1_6_3:
|
||||
case PROTO_VERSION_1_6_4:
|
||||
{
|
||||
// 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.
|
||||
m_Buffer.ResetRead();
|
||||
if (m_Buffer.CanReadBytes(2))
|
||||
{
|
||||
byte val;
|
||||
m_Buffer.ReadByte(val); // Packet type - Serverlist ping
|
||||
m_Buffer.ReadByte(val); // 0x01 magic value
|
||||
ASSERT(val == 0x01);
|
||||
}
|
||||
|
||||
// http://wiki.vg/wiki/index.php?title=Server_List_Ping&oldid=3100
|
||||
AString NumPlayers;
|
||||
Printf(NumPlayers, "%d", cRoot::Get()->GetServer()->GetNumPlayers());
|
||||
AString MaxPlayers;
|
||||
Printf(MaxPlayers, "%d", cRoot::Get()->GetServer()->GetMaxPlayers());
|
||||
|
||||
AString ProtocolVersionNum;
|
||||
Printf(ProtocolVersionNum, "%d", cRoot::Get()->GetPrimaryServerVersion());
|
||||
AString ProtocolVersionTxt(GetVersionTextFromInt(cRoot::Get()->GetPrimaryServerVersion()));
|
||||
|
||||
// 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()->GetServer()->GetDescription());
|
||||
Reply.push_back(0);
|
||||
Reply.append(NumPlayers);
|
||||
Reply.push_back(0);
|
||||
Reply.append(MaxPlayers);
|
||||
break;
|
||||
}
|
||||
} // switch (m_PrimaryServerVersion)
|
||||
m_Client->Kick(Reply);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
|
||||
// ProtocolRecognizer.h
|
||||
|
||||
// Interfaces to the cProtocolRecognizer class representing the meta-protocol that recognizes possibly multiple
|
||||
// protocol versions and redirects everything to them
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Protocol.h"
|
||||
#include "../ByteBuffer.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Adjust these if a new protocol is added or an old one is removed:
|
||||
#define MCS_CLIENT_VERSIONS "1.2.4, 1.2.5, 1.3.1, 1.3.2, 1.4.2, 1.4.4, 1.4.5, 1.4.6, 1.4.7, 1.5, 1.5.1, 1.5.2, 1.6.1, 1.6.2, 1.6.3, 1.6.4, 1.7.2"
|
||||
#define MCS_PROTOCOL_VERSIONS "29, 39, 47, 49, 51, 60, 61, 73, 74, 77, 78, 4"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cProtocolRecognizer :
|
||||
public cProtocol
|
||||
{
|
||||
typedef cProtocol super;
|
||||
|
||||
public:
|
||||
enum
|
||||
{
|
||||
PROTO_VERSION_1_2_5 = 29,
|
||||
PROTO_VERSION_1_3_2 = 39,
|
||||
PROTO_VERSION_1_4_2 = 47,
|
||||
PROTO_VERSION_1_4_4 = 49,
|
||||
PROTO_VERSION_1_4_6 = 51,
|
||||
PROTO_VERSION_1_5_0 = 60,
|
||||
PROTO_VERSION_1_5_2 = 61,
|
||||
PROTO_VERSION_1_6_1 = 73,
|
||||
PROTO_VERSION_1_6_2 = 74,
|
||||
PROTO_VERSION_1_6_3 = 77,
|
||||
PROTO_VERSION_1_6_4 = 78,
|
||||
|
||||
PROTO_VERSION_NEXT,
|
||||
PROTO_VERSION_LATEST = PROTO_VERSION_NEXT - 1, ///< Automatically assigned to the last protocol version, this serves as the default for PrimaryServerVersion
|
||||
|
||||
// These will be kept "under" the next / latest, because the next and latest are only needed for previous protocols
|
||||
PROTO_VERSION_1_7_2 = 4,
|
||||
} ;
|
||||
|
||||
cProtocolRecognizer(cClientHandle * a_Client);
|
||||
virtual ~cProtocolRecognizer();
|
||||
|
||||
/// Translates protocol version number into protocol version text: 49 -> "1.4.4"
|
||||
static AString GetVersionTextFromInt(int a_ProtocolVersion);
|
||||
|
||||
/// Called when client sends some data:
|
||||
virtual void DataReceived(const char * a_Data, int a_Size) override;
|
||||
|
||||
/// Sending stuff to clients (alphabetically sorted):
|
||||
virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override;
|
||||
virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override;
|
||||
virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override;
|
||||
virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
|
||||
virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override;
|
||||
virtual void SendChat (const AString & a_Message) override;
|
||||
virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override;
|
||||
virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override;
|
||||
virtual void SendDestroyEntity (const cEntity & a_Entity) override;
|
||||
virtual void SendDisconnect (const AString & a_Reason) override;
|
||||
virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) override; ///< Request the client to open up the sign editor for the sign (1.6+)
|
||||
virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override;
|
||||
virtual void SendEntityHeadLook (const cEntity & a_Entity) override;
|
||||
virtual void SendEntityLook (const cEntity & a_Entity) override;
|
||||
virtual void SendEntityMetadata (const cEntity & a_Entity) override;
|
||||
virtual void SendEntityProperties (const cEntity & a_Entity) override;
|
||||
virtual void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override;
|
||||
virtual void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override;
|
||||
virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) override;
|
||||
virtual void SendEntityVelocity (const cEntity & a_Entity) override;
|
||||
virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) override;
|
||||
virtual void SendGameMode (eGameMode a_GameMode) override;
|
||||
virtual void SendHealth (void) override;
|
||||
virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override;
|
||||
virtual void SendKeepAlive (int a_PingID) override;
|
||||
virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override;
|
||||
virtual void SendPickupSpawn (const cPickup & a_Pickup) override;
|
||||
virtual void SendPlayerAbilities (void) override;
|
||||
virtual void SendPlayerAnimation (const cPlayer & a_Player, char a_Animation) override;
|
||||
virtual void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline) override;
|
||||
virtual void SendPlayerMaxSpeed (void) override;
|
||||
virtual void SendPlayerMoveLook (void) override;
|
||||
virtual void SendPlayerPosition (void) override;
|
||||
virtual void SendPlayerSpawn (const cPlayer & a_Player) override;
|
||||
virtual void SendRespawn (void) override;
|
||||
virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override;
|
||||
virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override;
|
||||
virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override;
|
||||
virtual void SendSpawnMob (const cMonster & a_Mob) override;
|
||||
virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) override;
|
||||
virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) override;
|
||||
virtual void SendTabCompletionResults(const AStringVector & a_Results) override;
|
||||
virtual void SendTeleportEntity (const cEntity & a_Entity) override;
|
||||
virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override;
|
||||
virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) override;
|
||||
virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override;
|
||||
virtual void 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) override;
|
||||
virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) override;
|
||||
virtual void SendWeather (eWeather a_Weather) override;
|
||||
virtual void SendWholeInventory (const cWindow & a_Window) override;
|
||||
virtual void SendWindowClose (const cWindow & a_Window) override;
|
||||
virtual void SendWindowOpen (const cWindow & a_Window) override;
|
||||
virtual void SendWindowProperty (const cWindow & a_Window, short a_Property, short a_Value) override;
|
||||
|
||||
virtual AString GetAuthServerID(void) override;
|
||||
|
||||
virtual void SendData(const char * a_Data, int a_Size) override;
|
||||
|
||||
protected:
|
||||
cProtocol * m_Protocol; //< The recognized protocol
|
||||
cByteBuffer m_Buffer; //< Buffer for the incoming data until we recognize the protocol
|
||||
|
||||
/// Tries to recognize protocol based on m_Buffer contents; returns true if recognized
|
||||
bool TryRecognizeProtocol(void);
|
||||
|
||||
/** Tries to recognize a protocol in the length-less family, based on m_Buffer; returns true if recognized.
|
||||
Handles protocols before release 1.7, that didn't include packet lengths, and started with a 0x02 handshake packet
|
||||
Note that length-less server ping is handled directly in TryRecognizeProtocol(), this function is called only
|
||||
when the 0x02 Handshake packet has been received
|
||||
*/
|
||||
bool TryRecognizeLengthlessProtocol(void);
|
||||
|
||||
/** Tries to recognize a protocol in the leghted family (1.7+), based on m_Buffer; returns true if recognized.
|
||||
The packet length and type have already been read, type is 0
|
||||
The number of bytes remaining in the packet is passed as a_PacketLengthRemaining
|
||||
**/
|
||||
bool TryRecognizeLengthedProtocol(UInt32 a_PacketLengthRemaining);
|
||||
|
||||
/** Called when the recognizer gets a length-less protocol's server ping packet
|
||||
Responds with server stats and destroys the client.
|
||||
*/
|
||||
void SendLengthlessServerPing(void);
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user