1
0
Fork 0
cuberite-2a/src/Protocol/Protocol_1_8.cpp

3911 lines
102 KiB
C++
Raw Normal View History

2016-12-15 19:21:43 +00:00
// Protocol_1_8.cpp
/*
2016-12-15 19:21:43 +00:00
Implements the 1.8 protocol classes:
- cProtocol_1_8_0
- release 1.8 protocol (#47)
*/
#include "Globals.h"
#include "json/json.h"
2016-12-15 19:21:43 +00:00
#include "Protocol_1_8.h"
2014-09-08 17:24:33 +00:00
#include "ChunkDataSerializer.h"
#include "../mbedTLS++/Sha1Checksum.h"
#include "Packetizer.h"
#include "../ClientHandle.h"
#include "../Root.h"
#include "../Server.h"
#include "../World.h"
#include "../EffectID.h"
2014-09-08 17:24:33 +00:00
#include "../StringCompression.h"
#include "../CompositeChat.h"
#include "../Statistics.h"
2017-08-25 12:43:18 +00:00
#include "../UUID.h"
2020-03-29 15:54:37 +00:00
#include "../World.h"
#include "../JsonUtils.h"
2014-09-08 17:24:33 +00:00
#include "../WorldStorage/FastNBT.h"
#include "../WorldStorage/EnchantmentSerializer.h"
#include "../Entities/ExpOrb.h"
#include "../Entities/Minecart.h"
#include "../Entities/FallingBlock.h"
2020-04-20 19:46:04 +00:00
#include "../Entities/Floater.h"
#include "../Entities/Painting.h"
2014-09-08 17:24:33 +00:00
#include "../Entities/Pickup.h"
#include "../Entities/Player.h"
#include "../Entities/ItemFrame.h"
#include "../Entities/ArrowEntity.h"
#include "../Entities/FireworkEntity.h"
#include "../Mobs/IncludeAllMonsters.h"
#include "../UI/Window.h"
#include "../UI/HorseWindow.h"
2014-09-08 17:24:33 +00:00
#include "../BlockEntities/BeaconEntity.h"
#include "../BlockEntities/CommandBlockEntity.h"
#include "../BlockEntities/MobHeadEntity.h"
2014-11-18 14:33:41 +00:00
#include "../BlockEntities/MobSpawnerEntity.h"
2014-09-08 17:24:33 +00:00
#include "../BlockEntities/FlowerPotEntity.h"
#include "../Bindings/PluginManager.h"
2014-09-04 01:22:35 +00:00
#define HANDLE_READ(ByteBuf, Proc, Type, Var) \
Type Var; \
2019-08-11 09:39:43 +00:00
do { \
if (!ByteBuf.Proc(Var))\
{\
return;\
} \
} while (false)
2014-09-08 15:02:54 +00:00
#define HANDLE_PACKET_READ(ByteBuf, Proc, Type, Var) \
Type Var; \
2019-08-11 09:39:43 +00:00
do { \
2014-09-08 15:02:54 +00:00
{ \
2019-08-11 09:39:43 +00:00
if (!ByteBuf.Proc(Var)) \
{ \
ByteBuf.CheckValid(); \
return false; \
} \
2014-09-08 15:02:54 +00:00
ByteBuf.CheckValid(); \
} \
2019-08-11 09:39:43 +00:00
} while (false)
2014-09-08 15:02:54 +00:00
const int MAX_ENC_LEN = 512; // Maximum size of the encrypted message; should be 128, but who knows...
2014-09-19 13:07:01 +00:00
const uLongf MAX_COMPRESSED_PACKET_LEN = 200 KiB; // Maximum size of compressed packets.
2014-09-08 17:24:33 +00:00
// fwd: main.cpp:
extern bool g_ShouldLogCommIn, g_ShouldLogCommOut;
2014-09-08 17:24:33 +00:00
////////////////////////////////////////////////////////////////////////////////
2016-12-15 19:21:43 +00:00
// cProtocol_1_8_0:
2016-12-15 19:21:43 +00:00
cProtocol_1_8_0::cProtocol_1_8_0(cClientHandle * a_Client, const AString & a_ServerAddress, UInt16 a_ServerPort, UInt32 a_State) :
2020-04-13 16:38:06 +00:00
Super(a_Client),
2014-09-08 17:24:33 +00:00
m_ServerAddress(a_ServerAddress),
m_ServerPort(a_ServerPort),
m_State(a_State),
m_ReceivedData(32 KiB),
m_IsEncrypted(false)
2014-09-08 15:02:54 +00:00
{
AStringVector Params;
SplitZeroTerminatedStrings(a_ServerAddress, Params);
if (Params.size() >= 2)
{
m_ServerAddress = Params[0];
2017-08-25 12:43:18 +00:00
if (Params[1] == "FML")
{
LOGD("Forge client connected!");
m_Client->SetIsForgeClient();
}
else if (Params.size() == 4)
{
if (cRoot::Get()->GetServer()->ShouldAllowBungeeCord())
{
// BungeeCord handling:
// If BC is setup with ip_forward == true, it sends additional data in the login packet's ServerAddress field:
// hostname\00ip-address\00uuid\00profile-properties-as-json
2017-08-25 12:43:18 +00:00
LOGD("Player at %s connected via BungeeCord", Params[1].c_str());
m_Client->SetIPString(Params[1]);
cUUID UUID;
UUID.FromString(Params[2]);
m_Client->SetUUID(UUID);
Json::Value root;
if (!JsonUtils::ParseString(Params[3], root))
{
LOGERROR("Unable to parse player properties: '%s'", Params[3]);
}
else
{
m_Client->SetProperties(root);
}
}
else
{
LOG("BungeeCord is disabled, but client sent additional data, set AllowBungeeCord=1 if you want to allow it");
}
}
else
{
LOG("Unknown additional data sent in server address (BungeeCord/FML?): %zu parameters", Params.size());
// TODO: support FML + BungeeCord? (what parameters does it send in that case?) https://github.com/SpigotMC/BungeeCord/issues/899
}
}
2014-09-08 17:24:33 +00:00
// Create the comm log file, if so requested:
if (g_ShouldLogCommIn || g_ShouldLogCommOut)
2014-09-08 15:02:54 +00:00
{
2014-09-08 17:24:33 +00:00
static int sCounter = 0;
cFile::CreateFolder("CommLogs");
AString IP(a_Client->GetIPString());
ReplaceString(IP, ":", "_");
AString FileName = Printf("CommLogs/%x_%d__%s.log",
static_cast<unsigned>(time(nullptr)),
sCounter++,
IP.c_str()
);
if (!m_CommLogFile.Open(FileName, cFile::fmWrite))
{
LOG("Cannot log communication to file, the log file \"%s\" cannot be opened for writing.", FileName.c_str());
}
2014-09-08 15:02:54 +00:00
}
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::DataReceived(const char * a_Data, size_t a_Size)
2014-09-08 15:02:54 +00:00
{
2014-09-08 17:24:33 +00:00
if (m_IsEncrypted)
2014-09-08 15:02:54 +00:00
{
2014-09-08 17:24:33 +00:00
Byte Decrypted[512];
while (a_Size > 0)
{
size_t NumBytes = (a_Size > sizeof(Decrypted)) ? sizeof(Decrypted) : a_Size;
m_Decryptor.ProcessData(Decrypted, reinterpret_cast<const Byte *>(a_Data), NumBytes);
AddReceivedData(reinterpret_cast<const char *>(Decrypted), NumBytes);
2014-09-08 17:24:33 +00:00
a_Size -= NumBytes;
a_Data += NumBytes;
}
2014-09-08 15:02:54 +00:00
}
else
{
2014-09-08 17:24:33 +00:00
AddReceivedData(a_Data, a_Size);
2014-09-08 15:02:54 +00:00
}
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendAttachEntity(const cEntity & a_Entity, const cEntity & a_Vehicle)
{
ASSERT(m_State == 3); // In game mode?
2016-02-05 21:45:45 +00:00
cPacketizer Pkt(*this, pktAttachEntity);
Pkt.WriteBEUInt32(a_Entity.GetUniqueID());
1.9 / 1.9.2 / 1.9.3 / 1.9.4 protocol support (#3135) * Semistable update to 15w31a I'm going through snapshots in a sequential order since it should make things easier, and since protocol version history is written. * Update to 15w34b protocol Also, fix an issue with the Entity Equipment packet from the past version. Clients are able to connect and do stuff! * Partially update to 15w35e Chunk data doesn't work, but the client joins. I'm waiting to do chunk data because chunk data has an incomplete format until 15w36d. * Add '/blk' debug command This command lets one see what block they are looking at, and makes figuring out what's supposed to be where in a highly broken chunk possible. * Fix CRLF normalization in CheckBasicStyle.lua Normally, this doesn't cause an issue, but when running from cygwin, it detects the CR as whitespace and creates thousands of violations for every single line. Lua, when run on windows, will normalize automatically, but when run via cygwin, it won't. The bug was simply that gsub was returning a replaced version, but not changing the parameter, so the replaced version was ignored. * Update to 15w40b This includes chunk serialization. Fully functional chunk serialization for 1.9. I'm not completely happy with the chunk serialization as-is (correct use of palettes would be great), but cuberite also doesn't skip sending empty chunks so this performance optimization should probably come later. The creation of a full buffer is suboptimal, but it's the easiest way to implement this code. * Write long-by-long rather than creating a buffer This is a bit faster and should be equivalent. However, the code still doesn't look too good. * Update to 15w41a protocol This includes the new set passengers packet, which works off of the ridden entity, not the rider. That means, among other things, that information about the previously ridden vehicle is needed when detaching. So a new method with that info was added. * Update to 15w45a * 15w51b protocol * Update to 1.9.0 protocol Closes #3067. There are still a few things that need to be worked out (picking up items, effects, particles, and most importantly inventory), but in general this should work. I'll make a few more changes tomorrow to get the rest of the protocol set up, along with 1.9.1/1.9.2 (which did make a few changes). Chunks, however, _are_ working, along with most other parts of the game (placing/breaking blocks). * Fix item pickup packet not working That was a silly mistake, but at least it was an easy one. * 1.9.2 protocol support * Fix version info found in server list ping Thus, the client reports that it can connect rather than saying that the server is out of date. This required creating separate classes for 1.9.1 and 1.9.2, unfortunately. * Fix build errors generated by clang These didn't happen in MSVC. * Add protocol19x.cpp and protocol19x.h to CMakeLists * Ignore warnings in protocol19x that are ignored in protocol18x * Document BLOCK_FACE and DIG_STATUS constants * Fix BLOCK_FACE links and add separate section for DIG_STATUS * Fix bat animation and object spawning The causes of both of these are explained in #3135, but the gist is that both were typos. * Implement Use Item packet This means that buckets, bows, fishing rods, and several other similar items now work when not looking at a block. * Handle DIG_STATUS_SWAP_ITEM_IN_HAND * Add support for spawn eggs and potions The items are transformed from the 1.9 version to the 1.8 version when reading and transformed back when sending. * Remove spammy potion debug logging * Fix wolf collar color metadata The wrong type was being used, causing several clientside issues (including the screen going black). * Fix 1.9 chunk sending in the nether The nether and the end don't send skylight. * Fix clang build errors * Fix water bottles becoming mundane potions This happened because the can become splash potion bit got set incorrectly. Water bottles and mundane potions are only differentiated by the fact that water bottles have a metadata of 0, so setting that bit made it a mundane potion. Also add missing break statements to the read item NBT switch, which would otherwise break items with custom names and also cause incorrect "Unimplemented NBT data when parsing!" logging. * Copy Protocol18x as Protocol19x Aditionally, method and class names have been swapped to clean up other diffs. This commit is only added to make the following diffs more readable; it doesn't make any other changes (beyond class names). * Make thrown potions use the correct appearence This was caused by potions now using metadata. * Add missing api doc for cSplashPotionEntity::GetItem * Fix compile error in SplashPotionEntity.cpp * Fix fix of cSplashPotionEntity API doc * Temporarilly disable fall damage particles These were causing issues in 1.9 due to the changed effect ID. * Properly send a kick packet when connecting with an invalid version This means that the client no longer waits on the server screen with no indication whatsoever. However, right now the server list ping isn't implemented for unknown versions, so it'll only load "Old" on the ping. I also added a GetVarIntSize method to cByteBuffer. This helps clean up part of the code here (and I think it could clean up other parts), but it may make sense for it to be moved elsewhere (or declared in a different way). * Handle server list pings from unrecognized versions This isn't the cleanest way of writing it (it feels odd to use ProtocolRecognizer to send packets, and the addition of m_InPingForUnrecognizedVersion feels like the wrong technique), but it works and I can't think of a better way (apart from creating a full separate protocol class to handle only the ping... which would be worse). * Use cPacketizer for the disconnect packet This also should fix clang build errors. * Add 1.9.3 / 1.9.4 support * Fix incorrect indentation in APIDesc
2016-05-14 19:12:42 +00:00
Pkt.WriteBEUInt32(a_Vehicle.GetUniqueID());
Pkt.WriteBool(false);
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType)
2014-09-04 17:03:21 +00:00
{
2014-09-08 17:24:33 +00:00
ASSERT(m_State == 3); // In game mode?
2016-02-05 21:45:45 +00:00
cPacketizer Pkt(*this, pktBlockAction);
Pkt.WritePosition64(a_BlockX, a_BlockY, a_BlockZ);
Pkt.WriteBEInt8(a_Byte1);
Pkt.WriteBEInt8(a_Byte2);
Pkt.WriteVarInt32(a_BlockType);
2014-09-04 17:03:21 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendBlockBreakAnim(UInt32 a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage)
2014-09-04 01:22:35 +00:00
{
ASSERT(m_State == 3); // In game mode?
2016-02-05 21:45:45 +00:00
cPacketizer Pkt(*this, pktBlockBreakAnim);
Pkt.WriteVarInt32(a_EntityID);
Pkt.WritePosition64(a_BlockX, a_BlockY, a_BlockZ);
Pkt.WriteBEInt8(a_Stage);
2014-09-04 01:22:35 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
2014-09-04 01:22:35 +00:00
{
ASSERT(m_State == 3); // In game mode?
2014-09-08 17:24:33 +00:00
cPacketizer Pkt(*this, pktBlockChange);
Pkt.WritePosition64(a_BlockX, a_BlockY, a_BlockZ);
Pkt.WriteVarInt32((static_cast<UInt32>(a_BlockType) << 4) | (static_cast<UInt32>(a_BlockMeta) & 15));
2014-09-04 01:22:35 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes)
2014-09-04 01:22:35 +00:00
{
ASSERT(m_State == 3); // In game mode?
2014-09-08 17:24:33 +00:00
cPacketizer Pkt(*this, pktBlockChanges);
Pkt.WriteBEInt32(a_ChunkX);
Pkt.WriteBEInt32(a_ChunkZ);
Pkt.WriteVarInt32(static_cast<UInt32>(a_Changes.size()));
2014-09-08 17:24:33 +00:00
for (sSetBlockVector::const_iterator itr = a_Changes.begin(), end = a_Changes.end(); itr != end; ++itr)
{
Int16 Coords = static_cast<Int16>(itr->m_RelY | (itr->m_RelZ << 8) | (itr->m_RelX << 12));
Pkt.WriteBEInt16(Coords);
Pkt.WriteVarInt32(static_cast<UInt32>(itr->m_BlockType & 0xFFF) << 4 | (itr->m_BlockMeta & 0xF));
2014-09-09 16:27:31 +00:00
} // for itr - a_Changes[]
2014-09-04 01:22:35 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendCameraSetTo(const cEntity & a_Entity)
2016-10-12 12:38:45 +00:00
{
cPacketizer Pkt(*this, pktCameraSetTo);
2016-10-12 12:38:45 +00:00
Pkt.WriteVarInt32(a_Entity.GetUniqueID());
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendChat(const AString & a_Message, eChatType a_Type)
2014-09-04 01:22:35 +00:00
{
ASSERT(m_State == 3); // In game mode?
2016-02-05 21:45:45 +00:00
2016-12-15 19:21:43 +00:00
SendChatRaw(Printf("{\"text\":\"%s\"}", EscapeString(a_Message).c_str()), a_Type);
2014-09-04 01:22:35 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendChat(const cCompositeChat & a_Message, eChatType a_Type, bool a_ShouldUseChatPrefixes)
2014-09-04 01:22:35 +00:00
{
ASSERT(m_State == 3); // In game mode?
2016-12-15 19:21:43 +00:00
SendChatRaw(a_Message.CreateJsonString(a_ShouldUseChatPrefixes), a_Type);
2014-09-04 01:22:35 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendChatRaw(const AString & a_MessageRaw, eChatType a_Type)
{
ASSERT(m_State == 3); // In game mode?
// Send the json string to the client:
cPacketizer Pkt(*this, pktChatRaw);
Pkt.WriteString(a_MessageRaw);
Pkt.WriteBEInt8(a_Type);
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer)
2014-09-04 01:22:35 +00:00
{
ASSERT(m_State == 3); // In game mode?
2014-09-08 17:24:33 +00:00
// Serialize first, before creating the Packetizer (the packetizer locks a CS)
// This contains the flags and bitmasks, too
const AString & ChunkData = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_8_0, a_ChunkX, a_ChunkZ, {});
2014-09-08 17:24:33 +00:00
cCSLock Lock(m_CSPacket);
SendData(ChunkData.data(), ChunkData.size());
2014-09-04 01:22:35 +00:00
}
void cProtocol_1_8_0::SendCollectEntity(const cEntity & a_Collected, const cEntity & a_Collector, unsigned a_Count)
2014-09-04 01:22:35 +00:00
{
2016-12-15 19:21:43 +00:00
UNUSED(a_Count);
2014-09-04 01:22:35 +00:00
ASSERT(m_State == 3); // In game mode?
2016-02-05 21:45:45 +00:00
cPacketizer Pkt(*this, pktCollectEntity);
Pkt.WriteVarInt32(a_Collected.GetUniqueID());
Pkt.WriteVarInt32(a_Collector.GetUniqueID());
2014-09-04 01:22:35 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendDestroyEntity(const cEntity & a_Entity)
2014-09-04 01:22:35 +00:00
{
ASSERT(m_State == 3); // In game mode?
2016-02-05 21:45:45 +00:00
cPacketizer Pkt(*this, pktDestroyEntity);
Pkt.WriteVarInt32(1);
Pkt.WriteVarInt32(a_Entity.GetUniqueID());
2014-09-04 01:22:35 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendDetachEntity(const cEntity & a_Entity, const cEntity & a_PreviousVehicle)
1.9 / 1.9.2 / 1.9.3 / 1.9.4 protocol support (#3135) * Semistable update to 15w31a I'm going through snapshots in a sequential order since it should make things easier, and since protocol version history is written. * Update to 15w34b protocol Also, fix an issue with the Entity Equipment packet from the past version. Clients are able to connect and do stuff! * Partially update to 15w35e Chunk data doesn't work, but the client joins. I'm waiting to do chunk data because chunk data has an incomplete format until 15w36d. * Add '/blk' debug command This command lets one see what block they are looking at, and makes figuring out what's supposed to be where in a highly broken chunk possible. * Fix CRLF normalization in CheckBasicStyle.lua Normally, this doesn't cause an issue, but when running from cygwin, it detects the CR as whitespace and creates thousands of violations for every single line. Lua, when run on windows, will normalize automatically, but when run via cygwin, it won't. The bug was simply that gsub was returning a replaced version, but not changing the parameter, so the replaced version was ignored. * Update to 15w40b This includes chunk serialization. Fully functional chunk serialization for 1.9. I'm not completely happy with the chunk serialization as-is (correct use of palettes would be great), but cuberite also doesn't skip sending empty chunks so this performance optimization should probably come later. The creation of a full buffer is suboptimal, but it's the easiest way to implement this code. * Write long-by-long rather than creating a buffer This is a bit faster and should be equivalent. However, the code still doesn't look too good. * Update to 15w41a protocol This includes the new set passengers packet, which works off of the ridden entity, not the rider. That means, among other things, that information about the previously ridden vehicle is needed when detaching. So a new method with that info was added. * Update to 15w45a * 15w51b protocol * Update to 1.9.0 protocol Closes #3067. There are still a few things that need to be worked out (picking up items, effects, particles, and most importantly inventory), but in general this should work. I'll make a few more changes tomorrow to get the rest of the protocol set up, along with 1.9.1/1.9.2 (which did make a few changes). Chunks, however, _are_ working, along with most other parts of the game (placing/breaking blocks). * Fix item pickup packet not working That was a silly mistake, but at least it was an easy one. * 1.9.2 protocol support * Fix version info found in server list ping Thus, the client reports that it can connect rather than saying that the server is out of date. This required creating separate classes for 1.9.1 and 1.9.2, unfortunately. * Fix build errors generated by clang These didn't happen in MSVC. * Add protocol19x.cpp and protocol19x.h to CMakeLists * Ignore warnings in protocol19x that are ignored in protocol18x * Document BLOCK_FACE and DIG_STATUS constants * Fix BLOCK_FACE links and add separate section for DIG_STATUS * Fix bat animation and object spawning The causes of both of these are explained in #3135, but the gist is that both were typos. * Implement Use Item packet This means that buckets, bows, fishing rods, and several other similar items now work when not looking at a block. * Handle DIG_STATUS_SWAP_ITEM_IN_HAND * Add support for spawn eggs and potions The items are transformed from the 1.9 version to the 1.8 version when reading and transformed back when sending. * Remove spammy potion debug logging * Fix wolf collar color metadata The wrong type was being used, causing several clientside issues (including the screen going black). * Fix 1.9 chunk sending in the nether The nether and the end don't send skylight. * Fix clang build errors * Fix water bottles becoming mundane potions This happened because the can become splash potion bit got set incorrectly. Water bottles and mundane potions are only differentiated by the fact that water bottles have a metadata of 0, so setting that bit made it a mundane potion. Also add missing break statements to the read item NBT switch, which would otherwise break items with custom names and also cause incorrect "Unimplemented NBT data when parsing!" logging. * Copy Protocol18x as Protocol19x Aditionally, method and class names have been swapped to clean up other diffs. This commit is only added to make the following diffs more readable; it doesn't make any other changes (beyond class names). * Make thrown potions use the correct appearence This was caused by potions now using metadata. * Add missing api doc for cSplashPotionEntity::GetItem * Fix compile error in SplashPotionEntity.cpp * Fix fix of cSplashPotionEntity API doc * Temporarilly disable fall damage particles These were causing issues in 1.9 due to the changed effect ID. * Properly send a kick packet when connecting with an invalid version This means that the client no longer waits on the server screen with no indication whatsoever. However, right now the server list ping isn't implemented for unknown versions, so it'll only load "Old" on the ping. I also added a GetVarIntSize method to cByteBuffer. This helps clean up part of the code here (and I think it could clean up other parts), but it may make sense for it to be moved elsewhere (or declared in a different way). * Handle server list pings from unrecognized versions This isn't the cleanest way of writing it (it feels odd to use ProtocolRecognizer to send packets, and the addition of m_InPingForUnrecognizedVersion feels like the wrong technique), but it works and I can't think of a better way (apart from creating a full separate protocol class to handle only the ping... which would be worse). * Use cPacketizer for the disconnect packet This also should fix clang build errors. * Add 1.9.3 / 1.9.4 support * Fix incorrect indentation in APIDesc
2016-05-14 19:12:42 +00:00
{
ASSERT(m_State == 3); // In game mode?
cPacketizer Pkt(*this, pktAttachEntity);
1.9 / 1.9.2 / 1.9.3 / 1.9.4 protocol support (#3135) * Semistable update to 15w31a I'm going through snapshots in a sequential order since it should make things easier, and since protocol version history is written. * Update to 15w34b protocol Also, fix an issue with the Entity Equipment packet from the past version. Clients are able to connect and do stuff! * Partially update to 15w35e Chunk data doesn't work, but the client joins. I'm waiting to do chunk data because chunk data has an incomplete format until 15w36d. * Add '/blk' debug command This command lets one see what block they are looking at, and makes figuring out what's supposed to be where in a highly broken chunk possible. * Fix CRLF normalization in CheckBasicStyle.lua Normally, this doesn't cause an issue, but when running from cygwin, it detects the CR as whitespace and creates thousands of violations for every single line. Lua, when run on windows, will normalize automatically, but when run via cygwin, it won't. The bug was simply that gsub was returning a replaced version, but not changing the parameter, so the replaced version was ignored. * Update to 15w40b This includes chunk serialization. Fully functional chunk serialization for 1.9. I'm not completely happy with the chunk serialization as-is (correct use of palettes would be great), but cuberite also doesn't skip sending empty chunks so this performance optimization should probably come later. The creation of a full buffer is suboptimal, but it's the easiest way to implement this code. * Write long-by-long rather than creating a buffer This is a bit faster and should be equivalent. However, the code still doesn't look too good. * Update to 15w41a protocol This includes the new set passengers packet, which works off of the ridden entity, not the rider. That means, among other things, that information about the previously ridden vehicle is needed when detaching. So a new method with that info was added. * Update to 15w45a * 15w51b protocol * Update to 1.9.0 protocol Closes #3067. There are still a few things that need to be worked out (picking up items, effects, particles, and most importantly inventory), but in general this should work. I'll make a few more changes tomorrow to get the rest of the protocol set up, along with 1.9.1/1.9.2 (which did make a few changes). Chunks, however, _are_ working, along with most other parts of the game (placing/breaking blocks). * Fix item pickup packet not working That was a silly mistake, but at least it was an easy one. * 1.9.2 protocol support * Fix version info found in server list ping Thus, the client reports that it can connect rather than saying that the server is out of date. This required creating separate classes for 1.9.1 and 1.9.2, unfortunately. * Fix build errors generated by clang These didn't happen in MSVC. * Add protocol19x.cpp and protocol19x.h to CMakeLists * Ignore warnings in protocol19x that are ignored in protocol18x * Document BLOCK_FACE and DIG_STATUS constants * Fix BLOCK_FACE links and add separate section for DIG_STATUS * Fix bat animation and object spawning The causes of both of these are explained in #3135, but the gist is that both were typos. * Implement Use Item packet This means that buckets, bows, fishing rods, and several other similar items now work when not looking at a block. * Handle DIG_STATUS_SWAP_ITEM_IN_HAND * Add support for spawn eggs and potions The items are transformed from the 1.9 version to the 1.8 version when reading and transformed back when sending. * Remove spammy potion debug logging * Fix wolf collar color metadata The wrong type was being used, causing several clientside issues (including the screen going black). * Fix 1.9 chunk sending in the nether The nether and the end don't send skylight. * Fix clang build errors * Fix water bottles becoming mundane potions This happened because the can become splash potion bit got set incorrectly. Water bottles and mundane potions are only differentiated by the fact that water bottles have a metadata of 0, so setting that bit made it a mundane potion. Also add missing break statements to the read item NBT switch, which would otherwise break items with custom names and also cause incorrect "Unimplemented NBT data when parsing!" logging. * Copy Protocol18x as Protocol19x Aditionally, method and class names have been swapped to clean up other diffs. This commit is only added to make the following diffs more readable; it doesn't make any other changes (beyond class names). * Make thrown potions use the correct appearence This was caused by potions now using metadata. * Add missing api doc for cSplashPotionEntity::GetItem * Fix compile error in SplashPotionEntity.cpp * Fix fix of cSplashPotionEntity API doc * Temporarilly disable fall damage particles These were causing issues in 1.9 due to the changed effect ID. * Properly send a kick packet when connecting with an invalid version This means that the client no longer waits on the server screen with no indication whatsoever. However, right now the server list ping isn't implemented for unknown versions, so it'll only load "Old" on the ping. I also added a GetVarIntSize method to cByteBuffer. This helps clean up part of the code here (and I think it could clean up other parts), but it may make sense for it to be moved elsewhere (or declared in a different way). * Handle server list pings from unrecognized versions This isn't the cleanest way of writing it (it feels odd to use ProtocolRecognizer to send packets, and the addition of m_InPingForUnrecognizedVersion feels like the wrong technique), but it works and I can't think of a better way (apart from creating a full separate protocol class to handle only the ping... which would be worse). * Use cPacketizer for the disconnect packet This also should fix clang build errors. * Add 1.9.3 / 1.9.4 support * Fix incorrect indentation in APIDesc
2016-05-14 19:12:42 +00:00
Pkt.WriteBEUInt32(a_Entity.GetUniqueID());
Pkt.WriteBEUInt32(0);
Pkt.WriteBool(false);
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendDisconnect(const AString & a_Reason)
2014-09-08 17:24:33 +00:00
{
switch (m_State)
{
case 2:
{
// During login:
cPacketizer Pkt(*this, pktDisconnectDuringLogin);
2014-09-08 17:24:33 +00:00
Pkt.WriteString(Printf("{\"text\":\"%s\"}", EscapeString(a_Reason).c_str()));
break;
}
case 3:
{
// In-game:
cPacketizer Pkt(*this, pktDisconnectDuringGame);
2014-09-08 17:24:33 +00:00
Pkt.WriteString(Printf("{\"text\":\"%s\"}", EscapeString(a_Reason).c_str()));
break;
}
}
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendEditSign(int a_BlockX, int a_BlockY, int a_BlockZ)
2014-09-04 01:22:35 +00:00
{
ASSERT(m_State == 3); // In game mode?
2016-02-05 21:45:45 +00:00
cPacketizer Pkt(*this, pktEditSign);
Pkt.WritePosition64(a_BlockX, a_BlockY, a_BlockZ);
2014-09-08 17:24:33 +00:00
}
void cProtocol_1_8_0::SendEntityAnimation(const cEntity & a_Entity, char a_Animation)
{
ASSERT(m_State == 3); // In game mode?
cPacketizer Pkt(*this, pktEntityAnimation);
Pkt.WriteVarInt32(a_Entity.GetUniqueID());
Pkt.WriteBEInt8(a_Animation);
}
void cProtocol_1_8_0::SendEntityEffect(const cEntity & a_Entity, int a_EffectID, int a_Amplifier, int a_Duration)
2014-09-08 17:24:33 +00:00
{
ASSERT(m_State == 3); // In game mode?
2016-02-05 21:45:45 +00:00
cPacketizer Pkt(*this, pktEntityEffect);
Pkt.WriteVarInt32(a_Entity.GetUniqueID());
Pkt.WriteBEUInt8(static_cast<UInt8>(a_EffectID));
Pkt.WriteBEUInt8(static_cast<UInt8>(a_Amplifier));
Pkt.WriteVarInt32(static_cast<UInt32>(a_Duration));
2014-09-08 17:24:33 +00:00
Pkt.WriteBool(false); // Hide particles
2014-09-04 01:22:35 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item)
2014-09-04 01:22:35 +00:00
{
ASSERT(m_State == 3); // In game mode?
2016-02-05 21:45:45 +00:00
cPacketizer Pkt(*this, pktEntityEquipment);
Pkt.WriteVarInt32(a_Entity.GetUniqueID());
Pkt.WriteBEInt16(a_SlotNum);
WriteItem(Pkt, a_Item);
2014-09-04 01:22:35 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendEntityHeadLook(const cEntity & a_Entity)
2014-09-04 01:22:35 +00:00
{
ASSERT(m_State == 3); // In game mode?
2016-02-05 21:45:45 +00:00
cPacketizer Pkt(*this, pktEntityHeadLook);
Pkt.WriteVarInt32(a_Entity.GetUniqueID());
2014-09-09 16:27:31 +00:00
Pkt.WriteByteAngle(a_Entity.GetHeadYaw());
2014-09-04 01:22:35 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendEntityLook(const cEntity & a_Entity)
2014-09-04 01:22:35 +00:00
{
ASSERT(m_State == 3); // In game mode?
2016-02-05 21:45:45 +00:00
cPacketizer Pkt(*this, pktEntityLook);
Pkt.WriteVarInt32(a_Entity.GetUniqueID());
2014-09-08 17:24:33 +00:00
Pkt.WriteByteAngle(a_Entity.GetYaw());
Pkt.WriteByteAngle(a_Entity.GetPitch());
2015-02-06 20:40:20 +00:00
Pkt.WriteBool(a_Entity.IsOnGround());
2014-09-04 01:22:35 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendEntityMetadata(const cEntity & a_Entity)
2014-09-04 01:22:35 +00:00
{
ASSERT(m_State == 3); // In game mode?
2016-02-05 21:45:45 +00:00
cPacketizer Pkt(*this, pktEntityMeta);
Pkt.WriteVarInt32(a_Entity.GetUniqueID());
WriteEntityMetadata(Pkt, a_Entity);
Pkt.WriteBEUInt8(0x7f); // The termination byte
2014-09-04 01:22:35 +00:00
}
void cProtocol_1_8_0::SendEntityPosition(const cEntity & a_Entity)
2014-09-04 01:22:35 +00:00
{
ASSERT(m_State == 3); // In game mode?
2016-02-05 21:45:45 +00:00
const auto Delta = (a_Entity.GetPosition() - a_Entity.GetLastSentPosition()) * 32;
2014-09-04 01:22:35 +00:00
// Limitations of a byte
static const auto Max = std::numeric_limits<Int8>::max();
2014-09-04 01:22:35 +00:00
if ((std::abs(Delta.x) <= Max) && (std::abs(Delta.y) <= Max) && (std::abs(Delta.z) <= Max))
{
const auto Move = static_cast<Vector3<Int8>>(Delta);
2014-09-04 01:22:35 +00:00
// Difference within limitations, use a relative move packet
if (a_Entity.IsOrientationDirty())
{
cPacketizer Pkt(*this, pktEntityRelMoveLook);
Pkt.WriteVarInt32(a_Entity.GetUniqueID());
Pkt.WriteBEInt8(Move.x);
Pkt.WriteBEInt8(Move.y);
Pkt.WriteBEInt8(Move.z);
Pkt.WriteByteAngle(a_Entity.GetYaw());
Pkt.WriteByteAngle(a_Entity.GetPitch());
Pkt.WriteBool(a_Entity.IsOnGround());
}
else
{
cPacketizer Pkt(*this, pktEntityRelMove);
Pkt.WriteVarInt32(a_Entity.GetUniqueID());
Pkt.WriteBEInt8(Move.x);
Pkt.WriteBEInt8(Move.y);
Pkt.WriteBEInt8(Move.z);
Pkt.WriteBool(a_Entity.IsOnGround());
}
2014-09-04 01:22:35 +00:00
return;
}
2016-02-05 21:45:45 +00:00
// Too big a movement, do a teleport
SendEntityTeleport(a_Entity);
2014-09-04 01:22:35 +00:00
}
void cProtocol_1_8_0::SendEntityProperties(const cEntity & a_Entity)
2014-09-04 01:22:35 +00:00
{
ASSERT(m_State == 3); // In game mode?
2016-02-05 21:45:45 +00:00
cPacketizer Pkt(*this, pktEntityProperties);
Pkt.WriteVarInt32(a_Entity.GetUniqueID());
WriteEntityProperties(Pkt, a_Entity);
2014-09-04 01:22:35 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendEntityStatus(const cEntity & a_Entity, char a_Status)
2014-09-04 01:22:35 +00:00
{
ASSERT(m_State == 3); // In game mode?
2016-02-05 21:45:45 +00:00
cPacketizer Pkt(*this, pktEntityStatus);
Pkt.WriteBEUInt32(a_Entity.GetUniqueID());
Pkt.WriteBEInt8(a_Status);
2014-09-04 01:22:35 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendEntityVelocity(const cEntity & a_Entity)
2014-09-04 01:22:35 +00:00
{
ASSERT(m_State == 3); // In game mode?
2016-02-05 21:45:45 +00:00
cPacketizer Pkt(*this, pktEntityVelocity);
Pkt.WriteVarInt32(a_Entity.GetUniqueID());
// 400 = 8000 / 20 ... Conversion from our speed in m / s to 8000 m / tick
Pkt.WriteBEInt16(static_cast<Int16>(a_Entity.GetSpeedX() * 400));
Pkt.WriteBEInt16(static_cast<Int16>(a_Entity.GetSpeedY() * 400));
Pkt.WriteBEInt16(static_cast<Int16>(a_Entity.GetSpeedZ() * 400));
2014-09-04 01:22:35 +00:00
}
void cProtocol_1_8_0::SendExperience(void)
{
ASSERT(m_State == 3); // In game mode?
cPacketizer Pkt(*this, pktExperience);
cPlayer * Player = m_Client->GetPlayer();
Pkt.WriteBEFloat(Player->GetXpPercentage());
Pkt.WriteVarInt32(static_cast<UInt32>(Player->GetXpLevel()));
Pkt.WriteVarInt32(static_cast<UInt32>(Player->GetCurrentXp()));
}
void cProtocol_1_8_0::SendExperienceOrb(const cExpOrb & a_ExpOrb)
{
ASSERT(m_State == 3); // In game mode?
cPacketizer Pkt(*this, pktSpawnExperienceOrb);
Pkt.WriteVarInt32(a_ExpOrb.GetUniqueID());
Pkt.WriteFPInt(a_ExpOrb.GetPosX());
Pkt.WriteFPInt(a_ExpOrb.GetPosY());
Pkt.WriteFPInt(a_ExpOrb.GetPosZ());
Pkt.WriteBEInt16(static_cast<Int16>(a_ExpOrb.GetReward()));
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendExplosion(double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion)
2014-09-04 01:22:35 +00:00
{
ASSERT(m_State == 3); // In game mode?
2016-02-05 21:45:45 +00:00
cPacketizer Pkt(*this, pktExplosion);
Pkt.WriteBEFloat(static_cast<float>(a_BlockX));
Pkt.WriteBEFloat(static_cast<float>(a_BlockY));
Pkt.WriteBEFloat(static_cast<float>(a_BlockZ));
Pkt.WriteBEFloat(static_cast<float>(a_Radius));
Pkt.WriteBEUInt32(static_cast<UInt32>(a_BlocksAffected.size()));
2014-09-08 17:24:33 +00:00
for (cVector3iArray::const_iterator itr = a_BlocksAffected.begin(), end = a_BlocksAffected.end(); itr != end; ++itr)
{
Pkt.WriteBEInt8(static_cast<Int8>(itr->x));
Pkt.WriteBEInt8(static_cast<Int8>(itr->y));
Pkt.WriteBEInt8(static_cast<Int8>(itr->z));
2014-09-08 17:24:33 +00:00
} // for itr - a_BlockAffected[]
Pkt.WriteBEFloat(static_cast<float>(a_PlayerMotion.x));
Pkt.WriteBEFloat(static_cast<float>(a_PlayerMotion.y));
Pkt.WriteBEFloat(static_cast<float>(a_PlayerMotion.z));
2014-09-04 01:22:35 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendGameMode(eGameMode a_GameMode)
2014-09-04 01:22:35 +00:00
{
ASSERT(m_State == 3); // In game mode?
2016-02-05 21:45:45 +00:00
cPacketizer Pkt(*this, pktGameMode);
Pkt.WriteBEUInt8(3); // Reason: Change game mode
Pkt.WriteBEFloat(static_cast<float>(a_GameMode)); // The protocol really represents the value with a float!
2014-09-04 01:22:35 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendHealth(void)
2014-09-04 01:22:35 +00:00
{
ASSERT(m_State == 3); // In game mode?
cPacketizer Pkt(*this, pktUpdateHealth);
2014-09-08 17:24:33 +00:00
cPlayer * Player = m_Client->GetPlayer();
Pkt.WriteBEFloat(static_cast<float>(Player->GetHealth()));
Pkt.WriteVarInt32(static_cast<UInt32>(Player->GetFoodLevel()));
Pkt.WriteBEFloat(static_cast<float>(Player->GetFoodSaturationLevel()));
2014-09-04 01:22:35 +00:00
}
void cProtocol_1_8_0::SendHeldItemChange(int a_ItemIndex)
{
ASSERT((a_ItemIndex >= 0) && (a_ItemIndex <= 8)); // Valid check
cPacketizer Pkt(*this, pktHeldItemChange);
cPlayer * Player = m_Client->GetPlayer();
Pkt.WriteBEInt8(static_cast<Int8>(Player->GetInventory().GetEquippedSlotNum()));
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendHideTitle(void)
2015-04-07 14:41:19 +00:00
{
ASSERT(m_State == 3); // In game mode?
cPacketizer Pkt(*this, pktTitle);
2015-04-07 14:41:19 +00:00
Pkt.WriteVarInt32(3); // Hide title
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem & a_Item)
{
ASSERT(m_State == 3); // In game mode?
2016-02-05 21:45:45 +00:00
cPacketizer Pkt(*this, pktInventorySlot);
Pkt.WriteBEInt8(a_WindowID);
Pkt.WriteBEInt16(a_SlotNum);
WriteItem(Pkt, a_Item);
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendKeepAlive(UInt32 a_PingID)
{
2014-09-08 17:24:33 +00:00
// Drop the packet if the protocol is not in the Game state yet (caused a client crash):
if (m_State != 3)
{
2014-09-08 17:24:33 +00:00
LOGWARNING("Trying to send a KeepAlive packet to a player who's not yet fully logged in (%d). The protocol class prevented the packet.", m_State);
return;
}
2016-02-05 21:45:45 +00:00
cPacketizer Pkt(*this, pktKeepAlive);
Pkt.WriteVarInt32(a_PingID);
}
2017-08-21 08:46:41 +00:00
void cProtocol_1_8_0::SendLeashEntity(const cEntity & a_Entity, const cEntity & a_EntityLeashedTo)
{
ASSERT(m_State == 3); // In game mode?
cPacketizer Pkt(*this, pktLeashEntity);
2017-08-21 08:46:41 +00:00
Pkt.WriteBEUInt32(a_Entity.GetUniqueID());
Pkt.WriteBEUInt32(a_EntityLeashedTo.GetUniqueID());
Pkt.WriteBool(true);
}
void cProtocol_1_8_0::SendUnleashEntity(const cEntity & a_Entity)
{
ASSERT(m_State == 3); // In game mode?
cPacketizer Pkt(*this, pktLeashEntity);
2017-08-21 08:46:41 +00:00
Pkt.WriteBEUInt32(a_Entity.GetUniqueID());
Pkt.WriteBEInt32(-1);
Pkt.WriteBool(true);
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendLogin(const cPlayer & a_Player, const cWorld & a_World)
{
2014-09-08 17:24:33 +00:00
// Send the Join Game packet:
{
cServer * Server = cRoot::Get()->GetServer();
cPacketizer Pkt(*this, pktJoinGame);
Pkt.WriteBEUInt32(a_Player.GetUniqueID());
Pkt.WriteBEUInt8(static_cast<UInt8>(a_Player.GetEffectiveGameMode()) | (Server->IsHardcore() ? 0x08 : 0)); // Hardcore flag bit 4
Pkt.WriteBEInt8(static_cast<Int8>(a_World.GetDimension()));
Pkt.WriteBEUInt8(2); // TODO: Difficulty (set to Normal)
Pkt.WriteBEUInt8(static_cast<UInt8>(Clamp<size_t>(Server->GetMaxPlayers(), 0, 255)));
2014-09-08 17:24:33 +00:00
Pkt.WriteString("default"); // Level type - wtf?
Pkt.WriteBool(false); // Reduced Debug Info - wtf?
}
2016-02-05 21:45:45 +00:00
2014-09-08 17:24:33 +00:00
// Send the spawn position:
{
cPacketizer Pkt(*this, pktSpawnPosition);
Pkt.WritePosition64(FloorC(a_World.GetSpawnX()), FloorC(a_World.GetSpawnY()), FloorC(a_World.GetSpawnZ()));
2014-09-08 17:24:33 +00:00
}
2014-09-12 00:00:28 +00:00
// Send the server difficulty:
{
cPacketizer Pkt(*this, pktDifficulty);
Pkt.WriteBEInt8(1);
2014-09-12 00:00:28 +00:00
}
2016-02-05 21:45:45 +00:00
2014-09-08 17:24:33 +00:00
// Send player abilities:
SendPlayerAbilities();
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendLoginSuccess(void)
{
2014-09-08 17:24:33 +00:00
ASSERT(m_State == 2); // State: login?
2014-09-04 17:03:21 +00:00
2014-09-08 17:24:33 +00:00
// Enable compression:
{
cPacketizer Pkt(*this, pktStartCompression);
Pkt.WriteVarInt32(256);
2014-09-08 17:24:33 +00:00
}
m_State = 3; // State = Game
{
cPacketizer Pkt(*this, pktLoginSuccess);
2017-08-25 12:43:18 +00:00
Pkt.WriteString(m_Client->GetUUID().ToLongString());
2014-09-08 17:24:33 +00:00
Pkt.WriteString(m_Client->GetUsername());
}
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendPaintingSpawn(const cPainting & a_Painting)
2014-09-08 17:24:33 +00:00
{
ASSERT(m_State == 3); // In game mode?
2014-09-11 18:06:28 +00:00
double PosX = a_Painting.GetPosX();
double PosY = a_Painting.GetPosY();
double PosZ = a_Painting.GetPosZ();
cPacketizer Pkt(*this, pktSpawnPainting);
Pkt.WriteVarInt32(a_Painting.GetUniqueID());
2014-09-08 17:24:33 +00:00
Pkt.WriteString(a_Painting.GetName().c_str());
Pkt.WritePosition64(static_cast<Int32>(PosX), static_cast<Int32>(PosY), static_cast<Int32>(PosZ));
Pkt.WriteBEInt8(static_cast<Int8>(a_Painting.GetProtocolFacing()));
2014-09-08 17:24:33 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendMapData(const cMap & a_Map, int a_DataStartX, int a_DataStartY)
2014-09-08 17:24:33 +00:00
{
ASSERT(m_State == 3); // In game mode?
2016-02-05 21:45:45 +00:00
cPacketizer Pkt(*this, pktMapData);
2015-06-30 14:50:15 +00:00
Pkt.WriteVarInt32(a_Map.GetID());
Pkt.WriteBEUInt8(static_cast<UInt8>(a_Map.GetScale()));
2014-09-08 17:24:33 +00:00
2015-06-30 14:50:15 +00:00
Pkt.WriteVarInt32(static_cast<UInt32>(a_Map.GetDecorators().size()));
for (const auto & Decorator : a_Map.GetDecorators())
2014-09-08 17:24:33 +00:00
{
Pkt.WriteBEUInt8(static_cast<Byte>((static_cast<Int32>(Decorator.GetType()) << 4) | (Decorator.GetRot() & 0xF)));
Pkt.WriteBEUInt8(static_cast<UInt8>(Decorator.GetPixelX()));
Pkt.WriteBEUInt8(static_cast<UInt8>(Decorator.GetPixelZ()));
2014-09-08 17:24:33 +00:00
}
2015-06-30 14:50:15 +00:00
Pkt.WriteBEUInt8(128);
Pkt.WriteBEUInt8(128);
Pkt.WriteBEUInt8(static_cast<UInt8>(a_DataStartX));
Pkt.WriteBEUInt8(static_cast<UInt8>(a_DataStartY));
2015-06-30 14:50:15 +00:00
Pkt.WriteVarInt32(static_cast<UInt32>(a_Map.GetData().size()));
for (auto itr = a_Map.GetData().cbegin(); itr != a_Map.GetData().cend(); ++itr)
2014-09-08 17:24:33 +00:00
{
2015-06-30 14:50:15 +00:00
Pkt.WriteBEUInt8(*itr);
2014-09-08 17:24:33 +00:00
}
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendPlayerAbilities(void)
2014-09-08 17:24:33 +00:00
{
ASSERT(m_State == 3); // In game mode?
2016-02-05 21:45:45 +00:00
cPacketizer Pkt(*this, pktPlayerAbilities);
2014-09-08 17:24:33 +00:00
Byte Flags = 0;
cPlayer * Player = m_Client->GetPlayer();
if (Player->IsGameModeCreative())
{
Flags |= 0x01;
Flags |= 0x08; // Godmode, used for creative
}
if (Player->IsFlying())
{
Flags |= 0x02;
}
if (Player->CanFly())
{
Flags |= 0x04;
}
Pkt.WriteBEUInt8(Flags);
Pkt.WriteBEFloat(static_cast<float>(0.05 * Player->GetFlyingMaxSpeed()));
Pkt.WriteBEFloat(static_cast<float>(0.1 * Player->GetNormalMaxSpeed()));
2014-09-08 17:24:33 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendParticleEffect(const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmount)
2014-09-08 17:24:33 +00:00
{
ASSERT(m_State == 3); // In game mode?
2014-09-11 15:03:09 +00:00
int ParticleID = GetParticleID(a_ParticleName);
cPacketizer Pkt(*this, pktParticleEffect);
Pkt.WriteBEInt32(ParticleID);
2014-09-11 15:03:09 +00:00
Pkt.WriteBool(false);
Pkt.WriteBEFloat(a_SrcX);
Pkt.WriteBEFloat(a_SrcY);
Pkt.WriteBEFloat(a_SrcZ);
Pkt.WriteBEFloat(a_OffsetX);
Pkt.WriteBEFloat(a_OffsetY);
Pkt.WriteBEFloat(a_OffsetZ);
Pkt.WriteBEFloat(a_ParticleData);
Pkt.WriteBEInt32(a_ParticleAmount);
2014-09-08 17:24:33 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendParticleEffect(const AString & a_ParticleName, Vector3f a_Src, Vector3f a_Offset, float a_ParticleData, int a_ParticleAmount, std::array<int, 2> a_Data)
{
ASSERT(m_State == 3); // In game mode?
int ParticleID = GetParticleID(a_ParticleName);
cPacketizer Pkt(*this, pktParticleEffect);
Pkt.WriteBEInt32(ParticleID);
Pkt.WriteBool(false);
Pkt.WriteBEFloat(a_Src.x);
Pkt.WriteBEFloat(a_Src.y);
Pkt.WriteBEFloat(a_Src.z);
Pkt.WriteBEFloat(a_Offset.x);
Pkt.WriteBEFloat(a_Offset.y);
Pkt.WriteBEFloat(a_Offset.z);
Pkt.WriteBEFloat(a_ParticleData);
Pkt.WriteBEInt32(a_ParticleAmount);
switch (ParticleID)
{
// iconcrack
case 36:
{
Pkt.WriteVarInt32(static_cast<UInt32>(a_Data[0]));
Pkt.WriteVarInt32(static_cast<UInt32>(a_Data[1]));
break;
}
// blockcrack
// blockdust
case 37:
case 38:
{
Pkt.WriteVarInt32(static_cast<UInt32>(a_Data[0]));
break;
}
default:
{
break;
}
}
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendPlayerListAddPlayer(const cPlayer & a_Player)
2014-09-08 17:24:33 +00:00
{
ASSERT(m_State == 3); // In game mode?
cPacketizer Pkt(*this, pktPlayerList);
Pkt.WriteVarInt32(0);
Pkt.WriteVarInt32(1);
Pkt.WriteUUID(a_Player.GetUUID());
2014-09-26 15:37:19 +00:00
Pkt.WriteString(a_Player.GetPlayerListName());
const Json::Value & Properties = a_Player.GetClientHandle()->GetProperties();
Pkt.WriteVarInt32(Properties.size());
2015-10-19 14:03:55 +00:00
for (auto & Node : Properties)
{
2015-10-19 14:03:55 +00:00
Pkt.WriteString(Node.get("name", "").asString());
Pkt.WriteString(Node.get("value", "").asString());
AString Signature = Node.get("signature", "").asString();
if (Signature.empty())
{
Pkt.WriteBool(false);
}
else
{
Pkt.WriteBool(true);
Pkt.WriteString(Signature);
}
}
Pkt.WriteVarInt32(static_cast<UInt32>(a_Player.GetEffectiveGameMode()));
Pkt.WriteVarInt32(static_cast<UInt32>(a_Player.GetClientHandle()->GetPing()));
2014-09-26 15:37:19 +00:00
Pkt.WriteBool(false);
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendPlayerListRemovePlayer(const cPlayer & a_Player)
{
ASSERT(m_State == 3); // In game mode?
cPacketizer Pkt(*this, pktPlayerList);
Pkt.WriteVarInt32(4);
Pkt.WriteVarInt32(1);
Pkt.WriteUUID(a_Player.GetUUID());
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendPlayerListUpdateGameMode(const cPlayer & a_Player)
{
ASSERT(m_State == 3); // In game mode?
cPacketizer Pkt(*this, pktPlayerList);
Pkt.WriteVarInt32(1);
Pkt.WriteVarInt32(1);
Pkt.WriteUUID(a_Player.GetUUID());
Pkt.WriteVarInt32(static_cast<UInt32>(a_Player.GetEffectiveGameMode()));
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendPlayerListUpdatePing(const cPlayer & a_Player)
{
ASSERT(m_State == 3); // In game mode?
auto ClientHandle = a_Player.GetClientHandlePtr();
if (ClientHandle != nullptr)
{
cPacketizer Pkt(*this, pktPlayerList);
Pkt.WriteVarInt32(2);
Pkt.WriteVarInt32(1);
Pkt.WriteUUID(a_Player.GetUUID());
Pkt.WriteVarInt32(static_cast<UInt32>(ClientHandle->GetPing()));
}
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendPlayerListUpdateDisplayName(const cPlayer & a_Player, const AString & a_CustomName)
{
ASSERT(m_State == 3); // In game mode?
cPacketizer Pkt(*this, pktPlayerList);
Pkt.WriteVarInt32(3);
Pkt.WriteVarInt32(1);
Pkt.WriteUUID(a_Player.GetUUID());
2014-09-26 15:37:19 +00:00
if (a_CustomName.empty())
{
Pkt.WriteBool(false);
}
else
{
Pkt.WriteBool(true);
2014-09-26 15:37:19 +00:00
Pkt.WriteString(Printf("{\"text\":\"%s\"}", a_CustomName.c_str()));
}
2014-09-08 17:24:33 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendPlayerMaxSpeed(void)
2014-09-08 17:24:33 +00:00
{
ASSERT(m_State == 3); // In game mode?
2016-02-05 21:45:45 +00:00
cPacketizer Pkt(*this, pktEntityProperties);
2014-09-08 17:24:33 +00:00
cPlayer * Player = m_Client->GetPlayer();
Pkt.WriteVarInt32(Player->GetUniqueID());
Pkt.WriteBEInt32(1); // Count
2014-09-08 17:24:33 +00:00
Pkt.WriteString("generic.movementSpeed");
// The default game speed is 0.1, multiply that value by the relative speed:
Pkt.WriteBEDouble(0.1 * Player->GetNormalMaxSpeed());
2014-09-08 17:24:33 +00:00
if (Player->IsSprinting())
{
Pkt.WriteVarInt32(1); // Modifier count
Pkt.WriteBEUInt64(0x662a6b8dda3e4c1c);
Pkt.WriteBEUInt64(0x881396ea6097278d); // UUID of the modifier
Pkt.WriteBEDouble(Player->GetSprintingMaxSpeed() - Player->GetNormalMaxSpeed());
Pkt.WriteBEUInt8(2);
2014-09-08 17:24:33 +00:00
}
else
{
Pkt.WriteVarInt32(0); // Modifier count
2014-09-08 17:24:33 +00:00
}
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendPlayerMoveLook(void)
2014-09-08 17:24:33 +00:00
{
ASSERT(m_State == 3); // In game mode?
2016-02-05 21:45:45 +00:00
cPacketizer Pkt(*this, pktPlayerMoveLook);
2014-09-08 17:24:33 +00:00
cPlayer * Player = m_Client->GetPlayer();
Pkt.WriteBEDouble(Player->GetPosX());
2015-07-21 20:25:37 +00:00
Pkt.WriteBEDouble(Player->GetPosY());
Pkt.WriteBEDouble(Player->GetPosZ());
Pkt.WriteBEFloat(static_cast<float>(Player->GetYaw()));
Pkt.WriteBEFloat(static_cast<float>(Player->GetPitch()));
Pkt.WriteBEUInt8(0);
2014-09-08 17:24:33 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendPlayerPosition(void)
2014-09-08 17:24:33 +00:00
{
// There is no dedicated packet for this, send the whole thing:
SendPlayerMoveLook();
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendPlayerSpawn(const cPlayer & a_Player)
2014-09-08 17:24:33 +00:00
{
// Called to spawn another player for the client
cPacketizer Pkt(*this, pktSpawnOtherPlayer);
Pkt.WriteVarInt32(a_Player.GetUniqueID());
2017-08-25 12:43:18 +00:00
Pkt.WriteUUID(a_Player.GetUUID());
Vector3d LastSentPos = a_Player.GetLastSentPosition();
Pkt.WriteFPInt(LastSentPos.x);
Pkt.WriteFPInt(LastSentPos.y + 0.001); // The "+ 0.001" is there because otherwise the player falls through the block they were standing on.
Pkt.WriteFPInt(LastSentPos.z);
2014-09-08 17:24:33 +00:00
Pkt.WriteByteAngle(a_Player.GetYaw());
Pkt.WriteByteAngle(a_Player.GetPitch());
short ItemType = a_Player.GetEquippedItem().IsEmpty() ? 0 : a_Player.GetEquippedItem().m_ItemType;
Pkt.WriteBEInt16(ItemType);
Pkt.WriteBEUInt8((3 << 5) | 6); // Metadata: float + index 6
Pkt.WriteBEFloat(static_cast<float>(a_Player.GetHealth()));
Pkt.WriteBEUInt8((4 << 5 | (2 & 0x1F)) & 0xFF);
2014-09-08 17:24:33 +00:00
Pkt.WriteString(a_Player.GetName());
Pkt.WriteBEUInt8(0x7f); // Metadata: end
2014-09-08 17:24:33 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendPluginMessage(const AString & a_Channel, const AString & a_Message)
2014-09-08 17:24:33 +00:00
{
ASSERT(m_State == 3); // In game mode?
2016-02-05 21:45:45 +00:00
cPacketizer Pkt(*this, pktPluginMessage);
2014-09-08 17:24:33 +00:00
Pkt.WriteString(a_Channel);
Pkt.WriteBuf(a_Message.data(), a_Message.size());
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendRemoveEntityEffect(const cEntity & a_Entity, int a_EffectID)
2014-09-08 17:24:33 +00:00
{
ASSERT(m_State == 3); // In game mode?
2016-02-05 21:45:45 +00:00
cPacketizer Pkt(*this, pktRemoveEntityEffect);
Pkt.WriteVarInt32(a_Entity.GetUniqueID());
Pkt.WriteBEUInt8(static_cast<UInt8>(a_EffectID));
2014-09-08 17:24:33 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendResetTitle(void)
2015-04-07 14:41:19 +00:00
{
ASSERT(m_State == 3); // In game mode?
cPacketizer Pkt(*this, pktTitle);
2015-04-07 14:41:19 +00:00
Pkt.WriteVarInt32(4); // Reset title
}
2020-04-07 21:23:54 +00:00
void cProtocol_1_8_0::SendResourcePack(const AString & a_ResourcePackUrl)
{
cPacketizer Pkt(*this, pktResourcePack);
cSha1Checksum Checksum;
Checksum.Update(reinterpret_cast<const Byte *>(a_ResourcePackUrl.c_str()), a_ResourcePackUrl.size());
Byte Digest[20];
Checksum.Finalize(Digest);
AString Sha1Output;
cSha1Checksum::DigestToHex(Digest, Sha1Output);
Pkt.WriteString(a_ResourcePackUrl);
Pkt.WriteString(Sha1Output);
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendRespawn(eDimension a_Dimension)
2014-09-08 17:24:33 +00:00
{
cPacketizer Pkt(*this, pktRespawn);
2014-09-08 17:24:33 +00:00
cPlayer * Player = m_Client->GetPlayer();
Pkt.WriteBEInt32(static_cast<Int32>(a_Dimension));
Pkt.WriteBEUInt8(2); // TODO: Difficulty (set to Normal)
Pkt.WriteBEUInt8(static_cast<Byte>(Player->GetEffectiveGameMode()));
2014-09-08 17:24:33 +00:00
Pkt.WriteString("default");
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendScoreboardObjective(const AString & a_Name, const AString & a_DisplayName, Byte a_Mode)
2014-09-08 17:24:33 +00:00
{
ASSERT(m_State == 3); // In game mode?
2016-02-05 21:45:45 +00:00
cPacketizer Pkt(*this, pktScoreboardObjective);
2014-09-08 17:24:33 +00:00
Pkt.WriteString(a_Name);
Pkt.WriteBEUInt8(a_Mode);
2014-09-13 19:48:16 +00:00
if ((a_Mode == 0) || (a_Mode == 2))
{
Pkt.WriteString(a_DisplayName);
Pkt.WriteString("integer");
}
2014-09-08 17:24:33 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendScoreUpdate(const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode)
2014-09-08 17:24:33 +00:00
{
ASSERT(m_State == 3); // In game mode?
2016-02-05 21:45:45 +00:00
cPacketizer Pkt(*this, pktUpdateScore);
2014-09-08 17:24:33 +00:00
Pkt.WriteString(a_Player);
Pkt.WriteBEUInt8(a_Mode);
2014-09-13 19:48:16 +00:00
Pkt.WriteString(a_Objective);
2014-09-08 17:24:33 +00:00
if (a_Mode != 1)
{
Pkt.WriteVarInt32(static_cast<UInt32>(a_Score));
2014-09-08 17:24:33 +00:00
}
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendDisplayObjective(const AString & a_Objective, cScoreboard::eDisplaySlot a_Display)
2014-09-08 17:24:33 +00:00
{
ASSERT(m_State == 3); // In game mode?
2016-02-05 21:45:45 +00:00
cPacketizer Pkt(*this, pktDisplayObjective);
Pkt.WriteBEUInt8(static_cast<UInt8>(a_Display));
2014-09-08 17:24:33 +00:00
Pkt.WriteString(a_Objective);
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendSetSubTitle(const cCompositeChat & a_SubTitle)
2015-04-07 14:41:19 +00:00
{
SendSetRawSubTitle(a_SubTitle.CreateJsonString(false));
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendSetRawSubTitle(const AString & a_SubTitle)
2015-04-07 14:41:19 +00:00
{
ASSERT(m_State == 3); // In game mode?
cPacketizer Pkt(*this, pktTitle);
2015-04-07 14:41:19 +00:00
Pkt.WriteVarInt32(1); // Set subtitle
Pkt.WriteString(a_SubTitle);
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendSetTitle(const cCompositeChat & a_Title)
2015-04-07 14:41:19 +00:00
{
SendSetRawTitle(a_Title.CreateJsonString(false));
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendSetRawTitle(const AString & a_Title)
2015-04-07 14:41:19 +00:00
{
ASSERT(m_State == 3); // In game mode?
cPacketizer Pkt(*this, pktTitle);
2015-04-07 14:41:19 +00:00
Pkt.WriteVarInt32(0); // Set title
Pkt.WriteString(a_Title);
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendSoundEffect(const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch)
2014-09-08 17:24:33 +00:00
{
ASSERT(m_State == 3); // In game mode?
cPacketizer Pkt(*this, pktSoundEffect);
2014-09-08 17:24:33 +00:00
Pkt.WriteString(a_SoundName);
Pkt.WriteBEInt32(static_cast<Int32>(a_X * 8.0));
Pkt.WriteBEInt32(static_cast<Int32>(a_Y * 8.0));
Pkt.WriteBEInt32(static_cast<Int32>(a_Z * 8.0));
Pkt.WriteBEFloat(a_Volume);
Pkt.WriteBEUInt8(static_cast<Byte>(a_Pitch * 63));
2014-09-08 17:24:33 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendSoundParticleEffect(const EffectID a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data)
2014-09-08 17:24:33 +00:00
{
ASSERT(m_State == 3); // In game mode?
2016-02-05 21:45:45 +00:00
cPacketizer Pkt(*this, pktSoundParticleEffect);
Pkt.WriteBEInt32(static_cast<int>(a_EffectID));
Pkt.WritePosition64(a_SrcX, a_SrcY, a_SrcZ);
Pkt.WriteBEInt32(a_Data);
2014-09-08 17:24:33 +00:00
Pkt.WriteBool(false);
}
2020-04-20 19:46:04 +00:00
void cProtocol_1_8_0::SendSpawnEntity(const cEntity & a_Entity)
2014-09-08 17:24:33 +00:00
{
2020-04-20 19:46:04 +00:00
Int32 EntityData = /* Default: velocity present flag */ 1;
const auto EntityType = GetProtocolEntityType(a_Entity);
if (a_Entity.IsMinecart())
{
const auto & Cart = static_cast<const cMinecart &>(a_Entity);
EntityData = static_cast<Int32>(Cart.GetPayload());
}
else if (a_Entity.IsItemFrame())
{
const auto & Frame = static_cast<const cItemFrame &>(a_Entity);
EntityData = static_cast<Int32>(Frame.GetProtocolFacing());
}
else if (a_Entity.IsFallingBlock())
{
const auto & Block = static_cast<const cFallingBlock &>(a_Entity);
EntityData = Block.GetBlockType() | (static_cast<Int32>(Block.GetBlockMeta()) << 12);
}
else if (a_Entity.IsFloater())
{
const auto & Floater = static_cast<const cFloater &>(a_Entity);
EntityData = static_cast<Int32>(Floater.GetOwnerID());
}
else if (a_Entity.IsProjectile())
{
using PType = cProjectileEntity::eKind;
const auto & Projectile = static_cast<const cProjectileEntity &>(a_Entity);
if (Projectile.GetProjectileKind() == PType::pkArrow)
{
const auto & Arrow = static_cast<const cArrowEntity &>(Projectile);
EntityData = static_cast<Int32>(Arrow.GetCreatorUniqueID() + 1);
}
}
2016-02-05 21:45:45 +00:00
SendEntitySpawn(a_Entity, EntityType, EntityData);
2014-09-08 17:24:33 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendSpawnMob(const cMonster & a_Mob)
2014-09-08 17:24:33 +00:00
{
ASSERT(m_State == 3); // In game mode?
2016-02-05 21:45:45 +00:00
cPacketizer Pkt(*this, pktSpawnMob);
Pkt.WriteVarInt32(a_Mob.GetUniqueID());
2020-03-29 15:54:37 +00:00
Pkt.WriteBEUInt8(static_cast<Byte>(GetProtocolMobType(a_Mob.GetMobType())));
Vector3d LastSentPos = a_Mob.GetLastSentPosition();
Pkt.WriteFPInt(LastSentPos.x);
Pkt.WriteFPInt(LastSentPos.y);
Pkt.WriteFPInt(LastSentPos.z);
2014-09-08 17:24:33 +00:00
Pkt.WriteByteAngle(a_Mob.GetPitch());
Pkt.WriteByteAngle(a_Mob.GetHeadYaw());
Pkt.WriteByteAngle(a_Mob.GetYaw());
Pkt.WriteBEInt16(static_cast<Int16>(a_Mob.GetSpeedX() * 400));
Pkt.WriteBEInt16(static_cast<Int16>(a_Mob.GetSpeedY() * 400));
Pkt.WriteBEInt16(static_cast<Int16>(a_Mob.GetSpeedZ() * 400));
WriteEntityMetadata(Pkt, a_Mob);
Pkt.WriteBEUInt8(0x7f); // Metadata terminator
2014-09-08 17:24:33 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendStatistics(const cStatManager & a_Manager)
2014-09-08 17:24:33 +00:00
{
ASSERT(m_State == 3); // In game mode?
2016-02-05 21:45:45 +00:00
cPacketizer Pkt(*this, pktStatistics);
Pkt.WriteVarInt32(statCount); // TODO 2014-05-11 xdot: Optimization: Send "dirty" statistics only
2014-09-08 17:24:33 +00:00
size_t Count = static_cast<size_t>(statCount);
for (size_t i = 0; i < Count; ++i)
2014-09-08 17:24:33 +00:00
{
StatValue Value = a_Manager.GetValue(static_cast<eStatistic>(i));
const AString & StatName = cStatInfo::GetName(static_cast<eStatistic>(i));
2014-09-08 17:24:33 +00:00
Pkt.WriteString(StatName);
Pkt.WriteVarInt32(static_cast<UInt32>(Value));
2014-09-08 17:24:33 +00:00
}
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendTabCompletionResults(const AStringVector & a_Results)
2014-09-08 17:24:33 +00:00
{
ASSERT(m_State == 3); // In game mode?
2016-02-05 21:45:45 +00:00
cPacketizer Pkt(*this, pktTabCompletionResults);
Pkt.WriteVarInt32(static_cast<UInt32>(a_Results.size()));
2014-09-08 17:24:33 +00:00
for (AStringVector::const_iterator itr = a_Results.begin(), end = a_Results.end(); itr != end; ++itr)
{
Pkt.WriteString(*itr);
}
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ)
2014-09-08 17:24:33 +00:00
{
ASSERT(m_State == 3); // In game mode?
2016-02-05 21:45:45 +00:00
cPacketizer Pkt(*this, pktSpawnGlobalEntity);
Pkt.WriteVarInt32(0); // EntityID = 0, always
Pkt.WriteBEUInt8(1); // Type = Thunderbolt
2014-09-08 17:24:33 +00:00
Pkt.WriteFPInt(a_BlockX);
Pkt.WriteFPInt(a_BlockY);
Pkt.WriteFPInt(a_BlockZ);
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendTitleTimes(int a_FadeInTicks, int a_DisplayTicks, int a_FadeOutTicks)
2015-04-07 14:41:19 +00:00
{
ASSERT(m_State == 3); // In game mode?
cPacketizer Pkt(*this, pktTitle);
2015-04-07 14:41:19 +00:00
Pkt.WriteVarInt32(2); // Set title display times
Pkt.WriteBEInt32(a_FadeInTicks);
Pkt.WriteBEInt32(a_DisplayTicks);
Pkt.WriteBEInt32(a_FadeOutTicks);
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay, bool a_DoDaylightCycle)
2014-09-08 17:24:33 +00:00
{
ASSERT(m_State == 3); // In game mode?
if (!a_DoDaylightCycle)
{
// When writing a "-" before the number the client ignores it but it will stop the client-side time expiration.
a_TimeOfDay = std::min(-a_TimeOfDay, -1LL);
}
2016-02-05 21:45:45 +00:00
cPacketizer Pkt(*this, pktTimeUpdate);
Pkt.WriteBEInt64(a_WorldAge);
Pkt.WriteBEInt64(a_TimeOfDay);
2014-09-08 17:24:33 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendUnloadChunk(int a_ChunkX, int a_ChunkZ)
2014-09-08 17:24:33 +00:00
{
ASSERT(m_State == 3); // In game mode?
2016-02-05 21:45:45 +00:00
cPacketizer Pkt(*this, pktUnloadChunk);
Pkt.WriteBEInt32(a_ChunkX);
Pkt.WriteBEInt32(a_ChunkZ);
2014-09-08 17:24:33 +00:00
Pkt.WriteBool(true);
Pkt.WriteBEInt16(0); // Primary bitmap
Pkt.WriteVarInt32(0); // Data size
2014-09-08 17:24:33 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendUpdateBlockEntity(cBlockEntity & a_BlockEntity)
2014-09-08 17:24:33 +00:00
{
ASSERT(m_State == 3); // In game mode?
2016-02-05 21:45:45 +00:00
cPacketizer Pkt(*this, pktUpdateBlockEntity);
Pkt.WritePosition64(a_BlockEntity.GetPosX(), a_BlockEntity.GetPosY(), a_BlockEntity.GetPosZ());
2014-09-08 17:24:33 +00:00
Byte Action = 0;
switch (a_BlockEntity.GetBlockType())
{
case E_BLOCK_MOB_SPAWNER: Action = 1; break; // Update mob spawner spinny mob thing
case E_BLOCK_COMMAND_BLOCK: Action = 2; break; // Update command block text
case E_BLOCK_BEACON: Action = 3; break; // Update beacon entity
case E_BLOCK_HEAD: Action = 4; break; // Update Mobhead entity
case E_BLOCK_FLOWER_POT: Action = 5; break; // Update flower pot
case E_BLOCK_BED: Action = 11; break; // Update bed color
2014-09-08 17:24:33 +00:00
default: ASSERT(!"Unhandled or unimplemented BlockEntity update request!"); break;
}
Pkt.WriteBEUInt8(Action);
2014-09-08 17:24:33 +00:00
WriteBlockEntity(Pkt, a_BlockEntity);
2014-09-08 17:24:33 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::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)
2014-09-08 17:24:33 +00:00
{
ASSERT(m_State == 3); // In game mode?
cPacketizer Pkt(*this, pktUpdateSign);
Pkt.WritePosition64(a_BlockX, a_BlockY, a_BlockZ);
AString Lines[] = { a_Line1, a_Line2, a_Line3, a_Line4 };
for (size_t i = 0; i < ARRAYCOUNT(Lines); i++)
{
Json::Value RootValue;
RootValue["text"] = Lines[i];
Pkt.WriteString(JsonUtils::WriteFastString(RootValue));
}
2014-09-08 17:24:33 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ)
2014-09-08 17:24:33 +00:00
{
ASSERT(m_State == 3); // In game mode?
2016-02-05 21:45:45 +00:00
cPacketizer Pkt(*this, pktUseBed);
Pkt.WriteVarInt32(a_Entity.GetUniqueID());
Pkt.WritePosition64(a_BlockX, a_BlockY, a_BlockZ);
2014-09-08 17:24:33 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendWeather(eWeather a_Weather)
2014-09-08 17:24:33 +00:00
{
ASSERT(m_State == 3); // In game mode?
2016-02-05 21:45:45 +00:00
2014-09-08 17:24:33 +00:00
{
cPacketizer Pkt(*this, pktWeather);
Pkt.WriteBEUInt8((a_Weather == wSunny) ? 1 : 2); // End rain / begin rain
Pkt.WriteBEFloat(0); // Unused for weather
2014-09-08 17:24:33 +00:00
}
// TODO: Fade effect, somehow
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendWholeInventory(const cWindow & a_Window)
2014-09-08 17:24:33 +00:00
{
ASSERT(m_State == 3); // In game mode?
2016-02-05 21:45:45 +00:00
cPacketizer Pkt(*this, pktWindowItems);
2020-03-28 18:27:38 +00:00
Pkt.WriteBEUInt8(static_cast<UInt8>(a_Window.GetWindowID()));
Pkt.WriteBEInt16(static_cast<Int16>(a_Window.GetNumSlots()));
2014-09-08 17:24:33 +00:00
cItems Slots;
a_Window.GetSlots(*(m_Client->GetPlayer()), Slots);
for (cItems::const_iterator itr = Slots.begin(), end = Slots.end(); itr != end; ++itr)
{
WriteItem(Pkt, *itr);
2014-09-08 17:24:33 +00:00
} // for itr - Slots[]
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendWindowClose(const cWindow & a_Window)
2014-09-08 17:24:33 +00:00
{
ASSERT(m_State == 3); // In game mode?
2016-02-05 21:45:45 +00:00
cPacketizer Pkt(*this, pktWindowClose);
2020-03-28 18:27:38 +00:00
Pkt.WriteBEUInt8(static_cast<UInt8>(a_Window.GetWindowID()));
2014-09-08 17:24:33 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendWindowOpen(const cWindow & a_Window)
2014-09-08 17:24:33 +00:00
{
ASSERT(m_State == 3); // In game mode?
2014-09-08 17:24:33 +00:00
if (a_Window.GetWindowType() < 0)
{
// Do not send this packet for player inventory windows
return;
}
cPacketizer Pkt(*this, pktWindowOpen);
2020-03-28 18:27:38 +00:00
Pkt.WriteBEUInt8(static_cast<UInt8>(a_Window.GetWindowID()));
2014-09-11 21:17:27 +00:00
Pkt.WriteString(a_Window.GetWindowTypeName());
Pkt.WriteString(Printf("{\"text\":\"%s\"}", a_Window.GetWindowTitle().c_str()));
switch (a_Window.GetWindowType())
{
case cWindow::wtWorkbench:
case cWindow::wtEnchantment:
case cWindow::wtAnvil:
{
Pkt.WriteBEUInt8(0);
break;
}
default:
{
Pkt.WriteBEUInt8(static_cast<UInt8>(a_Window.GetNumNonInventorySlots()));
break;
}
}
2014-09-08 17:24:33 +00:00
if (a_Window.GetWindowType() == cWindow::wtAnimalChest)
{
UInt32 HorseID = static_cast<const cHorseWindow &>(a_Window).GetHorseID();
Pkt.WriteBEInt32(static_cast<Int32>(HorseID));
2014-09-08 17:24:33 +00:00
}
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendWindowProperty(const cWindow & a_Window, short a_Property, short a_Value)
2014-09-08 17:24:33 +00:00
{
ASSERT(m_State == 3); // In game mode?
2016-02-05 21:45:45 +00:00
cPacketizer Pkt(*this, pktWindowProperty);
2020-03-28 18:27:38 +00:00
Pkt.WriteBEUInt8(static_cast<UInt8>(a_Window.GetWindowID()));
Pkt.WriteBEInt16(a_Property);
Pkt.WriteBEInt16(a_Value);
2014-09-08 17:24:33 +00:00
}
2016-12-15 19:21:43 +00:00
bool cProtocol_1_8_0::CompressPacket(const AString & a_Packet, AString & a_CompressedData)
2014-09-09 16:27:31 +00:00
{
// Compress the data:
2014-09-19 13:07:01 +00:00
char CompressedData[MAX_COMPRESSED_PACKET_LEN];
2014-09-09 16:27:31 +00:00
uLongf CompressedSize = compressBound(static_cast<uLongf>(a_Packet.size()));
2014-09-19 13:07:01 +00:00
if (CompressedSize >= MAX_COMPRESSED_PACKET_LEN)
2014-09-09 16:27:31 +00:00
{
ASSERT(!"Too high packet size.");
return false;
}
int Status = compress2(
reinterpret_cast<Bytef *>(CompressedData), &CompressedSize,
reinterpret_cast<const Bytef *>(a_Packet.data()), static_cast<uLongf>(a_Packet.size()), Z_DEFAULT_COMPRESSION
);
2014-09-09 16:27:31 +00:00
if (Status != Z_OK)
{
return false;
}
AString LengthData;
cByteBuffer Buffer(20);
Buffer.WriteVarInt32(static_cast<UInt32>(a_Packet.size()));
2014-09-09 16:27:31 +00:00
Buffer.ReadAll(LengthData);
Buffer.CommitRead();
Buffer.WriteVarInt32(static_cast<UInt32>(CompressedSize + LengthData.size()));
Buffer.WriteVarInt32(static_cast<UInt32>(a_Packet.size()));
2014-09-09 16:27:31 +00:00
Buffer.ReadAll(LengthData);
Buffer.CommitRead();
a_CompressedData.clear();
2014-09-19 13:07:01 +00:00
a_CompressedData.reserve(LengthData.size() + CompressedSize);
2014-09-09 16:27:31 +00:00
a_CompressedData.append(LengthData.data(), LengthData.size());
a_CompressedData.append(CompressedData, CompressedSize);
return true;
}
2016-12-15 19:21:43 +00:00
int cProtocol_1_8_0::GetParticleID(const AString & a_ParticleName)
2014-09-11 15:03:09 +00:00
{
2020-04-20 19:46:04 +00:00
static const std::unordered_map<AString, int> ParticleMap
2014-09-11 15:03:09 +00:00
{
// Initialize the ParticleMap:
2020-04-20 19:46:04 +00:00
{ "explode", 0 },
{ "largeexplode", 1 },
{ "hugeexplosion", 2 },
{ "fireworksspark", 3 },
{ "bubble", 4 },
{ "splash", 5 },
{ "wake", 6 },
{ "suspended", 7 },
{ "depthsuspend", 8 },
{ "crit", 9 },
{ "magiccrit", 10 },
{ "smoke", 11 },
{ "largesmoke", 12 },
{ "spell", 13 },
{ "instantspell", 14 },
{ "mobspell", 15 },
{ "mobspellambient", 16 },
{ "witchmagic", 17 },
{ "dripwater", 18 },
{ "driplava", 19 },
{ "angryvillager", 20 },
{ "happyvillager", 21 },
{ "townaura", 22 },
{ "note", 23 },
{ "portal", 24 },
{ "enchantmenttable", 25 },
{ "flame", 26 },
{ "lava", 27 },
{ "footstep", 28 },
{ "cloud", 29 },
{ "reddust", 30 },
{ "snowballpoof", 31 },
{ "snowshovel", 32 },
{ "slime", 33 },
{ "heart", 34 },
{ "barrier", 35 },
{ "iconcrack", 36 },
{ "blockcrack", 37 },
{ "blockdust", 38 },
{ "droplet", 39 },
{ "take", 40 },
{ "mobappearance", 41 },
{ "dragonbreath", 42 },
{ "endrod", 43 },
{ "damageindicator", 44 },
{ "sweepattack", 45 },
{ "fallingdust", 46 },
{ "totem", 47 },
{ "spit", 48 }
};
const auto ParticleName = StrToLower(a_ParticleName);
const auto FindResult = ParticleMap.find(ParticleName);
if (FindResult == ParticleMap.end())
2014-09-11 15:03:09 +00:00
{
LOGWARNING("Unknown particle: %s", a_ParticleName.c_str());
2014-09-14 18:08:18 +00:00
ASSERT(!"Unknown particle");
2014-09-11 15:03:09 +00:00
return 0;
}
2020-04-20 19:46:04 +00:00
return FindResult->second;
2014-09-11 15:03:09 +00:00
}
2020-03-29 15:54:37 +00:00
UInt32 cProtocol_1_8_0::GetProtocolMobType(eMonsterType a_MobType)
{
switch (a_MobType)
{
// Map invalid type to Giant for easy debugging (if this ever spawns, something has gone very wrong)
2020-04-20 19:46:04 +00:00
case mtInvalidType: return 53;
2020-03-29 15:54:37 +00:00
case mtBat: return 65;
case mtBlaze: return 61;
case mtCaveSpider: return 59;
case mtChicken: return 93;
case mtCow: return 92;
case mtCreeper: return 50;
case mtEnderDragon: return 63;
case mtEnderman: return 58;
case mtGhast: return 56;
case mtGiant: return 53;
case mtGuardian: return 68;
case mtHorse: return 100;
case mtIronGolem: return 99;
case mtMagmaCube: return 62;
case mtMooshroom: return 96;
case mtOcelot: return 98;
case mtPig: return 90;
case mtRabbit: return 101;
case mtSheep: return 91;
case mtSilverfish: return 60;
case mtSkeleton: return 51;
case mtSlime: return 55;
case mtSnowGolem: return 97;
case mtSpider: return 52;
case mtSquid: return 94;
case mtVillager: return 120;
case mtWitch: return 66;
case mtWither: return 64;
2020-04-04 11:44:17 +00:00
case mtWitherSkeleton: return 51;
2020-03-29 15:54:37 +00:00
case mtWolf: return 95;
case mtZombie: return 54;
case mtZombiePigman: return 57;
case mtZombieVillager: return 27;
2020-03-29 15:54:37 +00:00
}
UNREACHABLE("Unsupported mob type");
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::AddReceivedData(const char * a_Data, size_t a_Size)
2014-09-08 17:24:33 +00:00
{
// Write the incoming data into the comm log file:
if (g_ShouldLogCommIn && m_CommLogFile.IsOpen())
2014-09-08 17:24:33 +00:00
{
if (m_ReceivedData.GetReadableSpace() > 0)
{
AString AllData;
size_t OldReadableSpace = m_ReceivedData.GetReadableSpace();
m_ReceivedData.ReadAll(AllData);
m_ReceivedData.ResetRead();
m_ReceivedData.SkipRead(m_ReceivedData.GetReadableSpace() - OldReadableSpace);
ASSERT(m_ReceivedData.GetReadableSpace() == OldReadableSpace);
AString Hex;
CreateHexDump(Hex, AllData.data(), AllData.size(), 16);
m_CommLogFile.Printf("Incoming data, %zu (0x%zx) unparsed bytes already present in buffer:\n%s\n",
2014-09-08 17:24:33 +00:00
AllData.size(), AllData.size(), Hex.c_str()
);
}
AString Hex;
CreateHexDump(Hex, a_Data, a_Size, 16);
m_CommLogFile.Printf("Incoming data: %u (0x%x) bytes: \n%s\n",
static_cast<unsigned>(a_Size), static_cast<unsigned>(a_Size), Hex.c_str()
2014-09-08 17:24:33 +00:00
);
m_CommLogFile.Flush();
}
if (!m_ReceivedData.Write(a_Data, a_Size))
{
// Too much data in the incoming queue, report to caller:
m_Client->PacketBufferFull();
return;
}
// Handle all complete packets:
for (;;)
{
UInt32 PacketLen;
if (!m_ReceivedData.ReadVarInt(PacketLen))
{
// Not enough data
m_ReceivedData.ResetRead();
break;
}
if (!m_ReceivedData.CanReadBytes(PacketLen))
{
// The full packet hasn't been received yet
m_ReceivedData.ResetRead();
break;
}
2016-02-05 21:45:45 +00:00
// Check packet for compression:
UInt32 UncompressedSize = 0;
AString UncompressedData;
2014-09-08 17:24:33 +00:00
if (m_State == 3)
{
UInt32 NumBytesRead = static_cast<UInt32>(m_ReceivedData.GetReadableSpace());
if (!m_ReceivedData.ReadVarInt(UncompressedSize))
{
m_Client->Kick("Compression packet incomplete");
return;
}
NumBytesRead -= static_cast<UInt32>(m_ReceivedData.GetReadableSpace()); // How many bytes has the UncompressedSize taken up?
ASSERT(PacketLen > NumBytesRead);
PacketLen -= NumBytesRead;
if (UncompressedSize > 0)
{
// Decompress the data:
AString CompressedData;
VERIFY(m_ReceivedData.ReadString(CompressedData, PacketLen));
if (InflateString(CompressedData.data(), PacketLen, UncompressedData) != Z_OK)
{
m_Client->Kick("Compression failure");
return;
}
PacketLen = static_cast<UInt32>(UncompressedData.size());
if (PacketLen != UncompressedSize)
{
m_Client->Kick("Wrong uncompressed packet size given");
return;
}
}
}
2016-02-05 21:45:45 +00:00
// Move the packet payload to a separate cByteBuffer, bb:
cByteBuffer bb(PacketLen + 1);
if (UncompressedSize == 0)
{
// No compression was used, move directly
VERIFY(m_ReceivedData.ReadToByteBuffer(bb, static_cast<size_t>(PacketLen)));
2014-09-08 17:24:33 +00:00
}
else
{
// Compression was used, move the uncompressed data:
VERIFY(bb.Write(UncompressedData.data(), UncompressedData.size()));
}
m_ReceivedData.CommitRead();
2014-09-08 17:24:33 +00:00
UInt32 PacketType;
if (!bb.ReadVarInt(PacketType))
{
// Not enough data
break;
}
// Write one NUL extra, so that we can detect over-reads
bb.Write("\0", 1);
2016-02-05 21:45:45 +00:00
2014-09-08 17:24:33 +00:00
// Log the packet info into the comm log file:
if (g_ShouldLogCommIn && m_CommLogFile.IsOpen())
2014-09-08 17:24:33 +00:00
{
AString PacketData;
bb.ReadAll(PacketData);
bb.ResetRead();
bb.ReadVarInt(PacketType); // We have already read the packet type once, it will be there again
ASSERT(PacketData.size() > 0); // We have written an extra NUL, so there had to be at least one byte read
2014-09-08 17:24:33 +00:00
PacketData.resize(PacketData.size() - 1);
AString PacketDataHex;
CreateHexDump(PacketDataHex, PacketData.data(), PacketData.size(), 16);
m_CommLogFile.Printf("Next incoming packet is type %u (0x%x), length %u (0x%x) at state %d. Payload:\n%s\n",
PacketType, PacketType, PacketLen, PacketLen, m_State, PacketDataHex.c_str()
);
}
if (!HandlePacket(bb, PacketType))
{
// Unknown packet, already been reported, but without the length. Log the length here:
LOGWARNING("Unhandled packet: type 0x%x, state %d, length %u", PacketType, m_State, PacketLen);
2016-02-05 21:45:45 +00:00
2014-09-08 17:24:33 +00:00
#ifdef _DEBUG
// Dump the packet contents into the log:
bb.ResetRead();
AString Packet;
bb.ReadAll(Packet);
Packet.resize(Packet.size() - 1); // Drop the final NUL pushed there for over-read detection
AString Out;
2016-01-06 15:20:12 +00:00
CreateHexDump(Out, Packet.data(), Packet.size(), 24);
2014-09-08 17:24:33 +00:00
LOGD("Packet contents:\n%s", Out.c_str());
#endif // _DEBUG
2016-02-05 21:45:45 +00:00
2014-09-08 17:24:33 +00:00
// Put a message in the comm log:
if (g_ShouldLogCommIn && m_CommLogFile.IsOpen())
{
2014-09-08 17:24:33 +00:00
m_CommLogFile.Printf("^^^^^^ Unhandled packet ^^^^^^\n\n\n");
}
2016-02-05 21:45:45 +00:00
2014-09-08 17:24:33 +00:00
return;
}
// The packet should have 1 byte left in the buffer - the NUL we had added
if (bb.GetReadableSpace() != 1)
2014-09-08 17:24:33 +00:00
{
// Read more or less than packet length, report as error
LOGWARNING("Protocol 1.8: Wrong number of bytes read for packet 0x%x, state %d. Read %zu bytes, packet contained %u bytes",
2014-09-08 17:24:33 +00:00
PacketType, m_State, bb.GetUsedSpace() - bb.GetReadableSpace(), PacketLen
);
// Put a message in the comm log:
if (g_ShouldLogCommIn && m_CommLogFile.IsOpen())
{
m_CommLogFile.Printf("^^^^^^ Wrong number of bytes read for this packet (exp %d left, got %zu left) ^^^^^^\n\n\n",
2014-09-08 17:24:33 +00:00
1, bb.GetReadableSpace()
);
m_CommLogFile.Flush();
}
ASSERT(!"Read wrong number of bytes!");
m_Client->PacketError(PacketType);
}
} // for (ever)
// Log any leftover bytes into the logfile:
if (g_ShouldLogCommIn && (m_ReceivedData.GetReadableSpace() > 0) && m_CommLogFile.IsOpen())
2014-09-08 17:24:33 +00:00
{
AString AllData;
size_t OldReadableSpace = m_ReceivedData.GetReadableSpace();
m_ReceivedData.ReadAll(AllData);
m_ReceivedData.ResetRead();
m_ReceivedData.SkipRead(m_ReceivedData.GetReadableSpace() - OldReadableSpace);
ASSERT(m_ReceivedData.GetReadableSpace() == OldReadableSpace);
AString Hex;
CreateHexDump(Hex, AllData.data(), AllData.size(), 16);
m_CommLogFile.Printf("There are %zu (0x%zx) bytes of non-parse-able data left in the buffer:\n%s",
2014-09-08 17:24:33 +00:00
m_ReceivedData.GetReadableSpace(), m_ReceivedData.GetReadableSpace(), Hex.c_str()
);
m_CommLogFile.Flush();
}
}
UInt32 cProtocol_1_8_0::GetPacketID(ePacketType a_PacketType)
{
switch (a_PacketType)
{
case pktAttachEntity: return 0x1b;
case pktBlockAction: return 0x24;
case pktBlockBreakAnim: return 0x25;
case pktBlockChange: return 0x23;
case pktBlockChanges: return 0x22;
case pktCameraSetTo: return 0x43;
case pktChatRaw: return 0x02;
case pktCollectEntity: return 0x0d;
case pktDestroyEntity: return 0x13;
case pktDifficulty: return 0x41;
case pktDisconnectDuringGame: return 0x40;
case pktDisconnectDuringLogin: return 0x00;
case pktDisplayObjective: return 0x3d;
case pktEditSign: return 0x36;
case pktEncryptionRequest: return 0x01;
case pktEntityAnimation: return 0x0b;
case pktEntityEffect: return 0x1d;
case pktEntityEquipment: return 0x04;
case pktEntityHeadLook: return 0x19;
case pktEntityLook: return 0x16;
case pktEntityMeta: return 0x1c;
case pktEntityProperties: return 0x20;
case pktEntityRelMove: return 0x15;
case pktEntityRelMoveLook: return 0x17;
case pktEntityStatus: return 0x1a;
case pktEntityVelocity: return 0x12;
case pktExperience: return 0x1f;
case pktExplosion: return 0x27;
case pktGameMode: return 0x2b;
case pktHeldItemChange: return 0x09;
case pktInventorySlot: return 0x2f;
case pktJoinGame: return 0x01;
case pktKeepAlive: return 0x00;
case pktLeashEntity: return 0x1b;
case pktLoginSuccess: return 0x02;
case pktMapData: return 0x34;
case pktParticleEffect: return 0x2a;
case pktPingResponse: return 0x01;
case pktPlayerAbilities: return 0x39;
case pktPlayerList: return 0x38;
case pktPlayerMoveLook: return 0x08;
case pktPluginMessage: return 0x3f;
case pktRemoveEntityEffect: return 0x1e;
2020-04-07 21:23:54 +00:00
case pktResourcePack: return 0x48;
case pktRespawn: return 0x07;
case pktScoreboardObjective: return 0x3b;
case pktSoundEffect: return 0x29;
case pktSoundParticleEffect: return 0x28;
case pktSpawnExperienceOrb: return 0x11;
case pktSpawnGlobalEntity: return 0x2c;
case pktSpawnMob: return 0x0f;
case pktSpawnObject: return 0x0e;
case pktSpawnOtherPlayer: return 0x0c;
case pktSpawnPainting: return 0x10;
case pktSpawnPosition: return 0x05;
case pktStartCompression: return 0x03;
case pktStatistics: return 0x37;
case pktStatusResponse: return 0x00;
case pktTabCompletionResults: return 0x3a;
case pktTeleportEntity: return 0x18;
case pktTimeUpdate: return 0x03;
case pktTitle: return 0x45;
case pktUnloadChunk: return 0x21;
case pktUpdateBlockEntity: return 0x35;
case pktUpdateHealth: return 0x06;
case pktUpdateScore: return 0x3c;
case pktUpdateSign: return 0x33;
case pktUseBed: return 0x0a;
case pktWeather: return 0x2b;
case pktWindowClose: return 0x2e;
case pktWindowItems: return 0x30;
case pktWindowOpen: return 0x2d;
case pktWindowProperty: return 0x31;
default:
{
LOG("Unhandled outgoing packet type: %s (0x%02x)", cPacketizer::PacketTypeToStr(a_PacketType), a_PacketType);
ASSERT(!"Unhandled outgoing packet type");
return 0;
}
}
}
2016-12-15 19:21:43 +00:00
bool cProtocol_1_8_0::HandlePacket(cByteBuffer & a_ByteBuffer, UInt32 a_PacketType)
2014-09-08 17:24:33 +00:00
{
switch (m_State)
{
case 1:
{
// Status
switch (a_PacketType)
{
case 0x00: HandlePacketStatusRequest(a_ByteBuffer); return true;
case 0x01: HandlePacketStatusPing (a_ByteBuffer); return true;
}
break;
}
2016-02-05 21:45:45 +00:00
2014-09-08 17:24:33 +00:00
case 2:
{
// Login
switch (a_PacketType)
{
case 0x00: HandlePacketLoginStart (a_ByteBuffer); return true;
case 0x01: HandlePacketLoginEncryptionResponse(a_ByteBuffer); return true;
}
break;
}
2016-02-05 21:45:45 +00:00
2014-09-08 17:24:33 +00:00
case 3:
{
// Game
switch (a_PacketType)
{
case 0x00: HandlePacketKeepAlive (a_ByteBuffer); return true;
case 0x01: HandlePacketChatMessage (a_ByteBuffer); return true;
case 0x02: HandlePacketUseEntity (a_ByteBuffer); return true;
case 0x03: HandlePacketPlayer (a_ByteBuffer); return true;
case 0x04: HandlePacketPlayerPos (a_ByteBuffer); return true;
case 0x05: HandlePacketPlayerLook (a_ByteBuffer); return true;
case 0x06: HandlePacketPlayerPosLook (a_ByteBuffer); return true;
case 0x07: HandlePacketBlockDig (a_ByteBuffer); return true;
case 0x08: HandlePacketBlockPlace (a_ByteBuffer); return true;
case 0x09: HandlePacketSlotSelect (a_ByteBuffer); return true;
case 0x0a: HandlePacketAnimation (a_ByteBuffer); return true;
case 0x0b: HandlePacketEntityAction (a_ByteBuffer); return true;
case 0x0c: HandlePacketSteerVehicle (a_ByteBuffer); return true;
case 0x0d: HandlePacketWindowClose (a_ByteBuffer); return true;
case 0x0e: HandlePacketWindowClick (a_ByteBuffer); return true;
case 0x0f: // Confirm transaction - not used in MCS
case 0x10: HandlePacketCreativeInventoryAction(a_ByteBuffer); return true;
case 0x11: HandlePacketEnchantItem (a_ByteBuffer); return true;
case 0x12: HandlePacketUpdateSign (a_ByteBuffer); return true;
case 0x13: HandlePacketPlayerAbilities (a_ByteBuffer); return true;
case 0x14: HandlePacketTabComplete (a_ByteBuffer); return true;
case 0x15: HandlePacketClientSettings (a_ByteBuffer); return true;
case 0x16: HandlePacketClientStatus (a_ByteBuffer); return true;
case 0x17: HandlePacketPluginMessage (a_ByteBuffer); return true;
2016-10-12 12:38:45 +00:00
case 0x18: HandlePacketSpectate (a_ByteBuffer); return true;
2020-04-07 21:23:54 +00:00
case 0x19: HandlePacketResourcePackStatus (a_ByteBuffer); return true;
}
2014-09-08 17:24:33 +00:00
break;
}
default:
{
// Received a packet in an unknown state, report:
LOGWARNING("Received a packet in an unknown protocol state %d. Ignoring further packets.", m_State);
2016-02-05 21:45:45 +00:00
2014-09-08 17:24:33 +00:00
// Cannot kick the client - we don't know this state and thus the packet number for the kick packet
2016-02-05 21:45:45 +00:00
2014-09-08 17:24:33 +00:00
// Switch to a state when all further packets are silently ignored:
m_State = 255;
return false;
}
case 255:
{
// This is the state used for "not processing packets anymore" when we receive a bad packet from a client.
// Do not output anything (the caller will do that for us), just return failure
return false;
}
} // switch (m_State)
2016-02-05 21:45:45 +00:00
2014-09-08 17:24:33 +00:00
// Unknown packet type, report to the ClientHandle:
m_Client->PacketUnknown(a_PacketType);
return false;
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::HandlePacketStatusPing(cByteBuffer & a_ByteBuffer)
2014-09-08 17:24:33 +00:00
{
HANDLE_READ(a_ByteBuffer, ReadBEInt64, Int64, Timestamp);
cPacketizer Pkt(*this, pktPingResponse);
Pkt.WriteBEInt64(Timestamp);
2014-09-08 17:24:33 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer)
2014-09-08 17:24:33 +00:00
{
cServer * Server = cRoot::Get()->GetServer();
AString ServerDescription = Server->GetDescription();
auto NumPlayers = static_cast<signed>(Server->GetNumPlayers());
auto MaxPlayers = static_cast<signed>(Server->GetMaxPlayers());
2014-09-08 17:24:33 +00:00
AString Favicon = Server->GetFaviconData();
cRoot::Get()->GetPluginManager()->CallHookServerPing(*m_Client, ServerDescription, NumPlayers, MaxPlayers, Favicon);
// Version:
Json::Value Version;
2015-09-25 08:14:17 +00:00
Version["name"] = "Cuberite 1.8";
2014-09-08 17:24:33 +00:00
Version["protocol"] = 47;
// Players:
Json::Value Players;
Players["online"] = NumPlayers;
Players["max"] = MaxPlayers;
// TODO: Add "sample"
// Description:
Json::Value Description;
Description["text"] = ServerDescription.c_str();
// Create the response:
Json::Value ResponseValue;
ResponseValue["version"] = Version;
ResponseValue["players"] = Players;
ResponseValue["description"] = Description;
m_Client->ForgeAugmentServerListPing(ResponseValue);
2014-09-08 17:24:33 +00:00
if (!Favicon.empty())
{
ResponseValue["favicon"] = Printf("data:image/png;base64,%s", Favicon.c_str());
}
auto Response = JsonUtils::WriteFastString(ResponseValue);
2014-09-08 17:24:33 +00:00
cPacketizer Pkt(*this, pktStatusResponse);
2014-09-08 17:24:33 +00:00
Pkt.WriteString(Response);
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::HandlePacketLoginEncryptionResponse(cByteBuffer & a_ByteBuffer)
2014-09-08 17:24:33 +00:00
{
UInt32 EncKeyLength, EncNonceLength;
if (!a_ByteBuffer.ReadVarInt(EncKeyLength))
{
return;
}
2014-09-08 17:24:33 +00:00
AString EncKey;
if (!a_ByteBuffer.ReadString(EncKey, EncKeyLength))
{
return;
}
if (!a_ByteBuffer.ReadVarInt(EncNonceLength))
{
return;
}
2014-09-08 17:24:33 +00:00
AString EncNonce;
if (!a_ByteBuffer.ReadString(EncNonce, EncNonceLength))
{
return;
}
if ((EncKeyLength > MAX_ENC_LEN) || (EncNonceLength > MAX_ENC_LEN))
{
LOGD("Too long encryption");
m_Client->Kick("Hacked client");
return;
}
// Decrypt EncNonce using privkey
cRsaPrivateKey & rsaDecryptor = cRoot::Get()->GetServer()->GetPrivateKey();
2016-01-06 15:20:12 +00:00
UInt32 DecryptedNonce[MAX_ENC_LEN / sizeof(Int32)];
int res = rsaDecryptor.Decrypt(reinterpret_cast<const Byte *>(EncNonce.data()), EncNonce.size(), reinterpret_cast<Byte *>(DecryptedNonce), sizeof(DecryptedNonce));
2014-09-08 17:24:33 +00:00
if (res != 4)
{
LOGD("Bad nonce length: got %d, exp %d", res, 4);
m_Client->Kick("Hacked client");
return;
}
if (ntohl(DecryptedNonce[0]) != static_cast<unsigned>(reinterpret_cast<uintptr_t>(this)))
2014-09-08 17:24:33 +00:00
{
LOGD("Bad nonce value");
m_Client->Kick("Hacked client");
return;
}
2016-02-05 21:45:45 +00:00
2014-09-08 17:24:33 +00:00
// Decrypt the symmetric encryption key using privkey:
Byte DecryptedKey[MAX_ENC_LEN];
res = rsaDecryptor.Decrypt(reinterpret_cast<const Byte *>(EncKey.data()), EncKey.size(), DecryptedKey, sizeof(DecryptedKey));
2014-09-08 17:24:33 +00:00
if (res != 16)
{
LOGD("Bad key length");
m_Client->Kick("Hacked client");
return;
}
2016-02-05 21:45:45 +00:00
2014-09-08 17:24:33 +00:00
StartEncryption(DecryptedKey);
m_Client->HandleLogin(m_Client->GetUsername());
2014-09-08 17:24:33 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::HandlePacketLoginStart(cByteBuffer & a_ByteBuffer)
2014-09-08 17:24:33 +00:00
{
AString Username;
if (!a_ByteBuffer.ReadVarUTF8String(Username))
{
m_Client->Kick("Bad username");
return;
}
2016-02-05 21:45:45 +00:00
2014-09-08 17:24:33 +00:00
if (!m_Client->HandleHandshake(Username))
{
// The client is not welcome here, they have been sent a Kick packet already
return;
}
2016-02-05 21:45:45 +00:00
2014-09-08 17:24:33 +00:00
cServer * Server = cRoot::Get()->GetServer();
// If auth is required, then send the encryption request:
if (Server->ShouldAuthenticate())
{
cPacketizer Pkt(*this, pktEncryptionRequest);
2014-09-08 17:24:33 +00:00
Pkt.WriteString(Server->GetServerID());
const AString & PubKeyDer = Server->GetPublicKeyDER();
Pkt.WriteVarInt32(static_cast<UInt32>(PubKeyDer.size()));
2014-09-08 17:24:33 +00:00
Pkt.WriteBuf(PubKeyDer.data(), PubKeyDer.size());
Pkt.WriteVarInt32(4);
Pkt.WriteBEInt32(static_cast<int>(reinterpret_cast<intptr_t>(this))); // Using 'this' as the cryptographic nonce, so that we don't have to generate one each time :)
2014-09-08 17:24:33 +00:00
m_Client->SetUsername(Username);
return;
}
2016-02-05 21:45:45 +00:00
m_Client->HandleLogin(Username);
2014-09-08 17:24:33 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::HandlePacketAnimation(cByteBuffer & a_ByteBuffer)
2014-09-08 17:24:33 +00:00
{
m_Client->HandleAnimation(0); // Packet exists solely for arm-swing notification
2014-09-08 17:24:33 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::HandlePacketBlockDig(cByteBuffer & a_ByteBuffer)
2014-09-08 17:24:33 +00:00
{
HANDLE_READ(a_ByteBuffer, ReadBEUInt8, UInt8, Status);
2014-09-08 17:24:33 +00:00
int BlockX, BlockY, BlockZ;
if (!a_ByteBuffer.ReadPosition64(BlockX, BlockY, BlockZ))
2014-09-08 17:24:33 +00:00
{
return;
}
HANDLE_READ(a_ByteBuffer, ReadBEInt8, Int8, Face);
m_Client->HandleLeftClick(BlockX, BlockY, BlockZ, FaceIntToBlockFace(Face), Status);
2014-09-08 17:24:33 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::HandlePacketBlockPlace(cByteBuffer & a_ByteBuffer)
2014-09-08 17:24:33 +00:00
{
int BlockX, BlockY, BlockZ;
if (!a_ByteBuffer.ReadPosition64(BlockX, BlockY, BlockZ))
2014-09-08 17:24:33 +00:00
{
return;
}
HANDLE_READ(a_ByteBuffer, ReadBEInt8, Int8, Face);
2014-09-08 18:12:43 +00:00
cItem Item; // Ignored
2014-09-22 20:06:08 +00:00
ReadItem(a_ByteBuffer, Item, 3);
2014-09-08 17:24:33 +00:00
HANDLE_READ(a_ByteBuffer, ReadBEUInt8, UInt8, CursorX);
HANDLE_READ(a_ByteBuffer, ReadBEUInt8, UInt8, CursorY);
HANDLE_READ(a_ByteBuffer, ReadBEUInt8, UInt8, CursorZ);
eBlockFace blockFace = FaceIntToBlockFace(Face);
if (blockFace == eBlockFace::BLOCK_FACE_NONE)
{
m_Client->HandleUseItem(eHand::hMain);
}
else
{
m_Client->HandleRightClick(BlockX, BlockY, BlockZ, blockFace, CursorX, CursorY, CursorZ, eHand::hMain);
}
2014-09-08 17:24:33 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::HandlePacketChatMessage(cByteBuffer & a_ByteBuffer)
2014-09-08 17:24:33 +00:00
{
HANDLE_READ(a_ByteBuffer, ReadVarUTF8String, AString, Message);
m_Client->HandleChat(Message);
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::HandlePacketClientSettings(cByteBuffer & a_ByteBuffer)
2014-09-08 17:24:33 +00:00
{
HANDLE_READ(a_ByteBuffer, ReadVarUTF8String, AString, Locale);
HANDLE_READ(a_ByteBuffer, ReadBEUInt8, UInt8, ViewDistance);
HANDLE_READ(a_ByteBuffer, ReadBEUInt8, UInt8, ChatFlags);
2014-09-08 17:24:33 +00:00
HANDLE_READ(a_ByteBuffer, ReadBool, bool, ChatColors);
HANDLE_READ(a_ByteBuffer, ReadBEUInt8, UInt8, SkinParts);
2016-02-05 21:45:45 +00:00
2014-09-08 17:24:33 +00:00
m_Client->SetLocale(Locale);
2014-10-02 21:50:41 +00:00
m_Client->SetViewDistance(ViewDistance);
m_Client->GetPlayer()->SetSkinParts(SkinParts);
// TODO: Handle chat flags and chat colors
2014-09-08 17:24:33 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::HandlePacketClientStatus(cByteBuffer & a_ByteBuffer)
2014-09-08 17:24:33 +00:00
{
HANDLE_READ(a_ByteBuffer, ReadBEUInt8, UInt8, ActionID);
2014-09-08 17:24:33 +00:00
switch (ActionID)
{
case 0:
{
// Respawn
m_Client->HandleRespawn();
break;
}
case 1:
{
// Request stats
const cStatManager & Manager = m_Client->GetPlayer()->GetStatManager();
SendStatistics(Manager);
2014-09-08 17:24:33 +00:00
break;
}
case 2:
{
// Open Inventory achievement
m_Client->GetPlayer()->AwardAchievement(achOpenInv);
break;
}
}
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::HandlePacketCreativeInventoryAction(cByteBuffer & a_ByteBuffer)
2014-09-08 17:24:33 +00:00
{
HANDLE_READ(a_ByteBuffer, ReadBEInt16, Int16, SlotNum);
2014-09-08 17:24:33 +00:00
cItem Item;
2014-09-22 20:06:08 +00:00
if (!ReadItem(a_ByteBuffer, Item))
2014-09-08 17:24:33 +00:00
{
return;
}
2015-05-16 21:22:50 +00:00
m_Client->HandleCreativeInventory(SlotNum, Item, (SlotNum == -1) ? caLeftClickOutside : caLeftClick);
2014-09-08 17:24:33 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::HandlePacketEntityAction(cByteBuffer & a_ByteBuffer)
2014-09-08 17:24:33 +00:00
{
HANDLE_READ(a_ByteBuffer, ReadVarInt, UInt32, PlayerID);
HANDLE_READ(a_ByteBuffer, ReadBEUInt8, UInt8, Action);
HANDLE_READ(a_ByteBuffer, ReadVarInt, UInt32, JumpBoost);
2014-09-08 17:24:33 +00:00
switch (Action)
{
case 0: m_Client->HandleEntityCrouch(PlayerID, true); break; // Crouch
case 1: m_Client->HandleEntityCrouch(PlayerID, false); break; // Uncrouch
case 2: m_Client->HandleEntityLeaveBed(PlayerID); break; // Leave Bed
case 3: m_Client->HandleEntitySprinting(PlayerID, true); break; // Start sprinting
case 4: m_Client->HandleEntitySprinting(PlayerID, false); break; // Stop sprinting
case 6: m_Client->HandleOpenHorseInventory(PlayerID); break; // Open horse inventory
2014-09-08 17:24:33 +00:00
}
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::HandlePacketKeepAlive(cByteBuffer & a_ByteBuffer)
2014-09-08 17:24:33 +00:00
{
HANDLE_READ(a_ByteBuffer, ReadVarInt, UInt32, KeepAliveID);
m_Client->HandleKeepAlive(KeepAliveID);
2014-09-08 17:24:33 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::HandlePacketPlayer(cByteBuffer & a_ByteBuffer)
2014-09-08 17:24:33 +00:00
{
HANDLE_READ(a_ByteBuffer, ReadBool, bool, IsOnGround);
// TODO: m_Client->HandlePlayerOnGround(IsOnGround);
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::HandlePacketPlayerAbilities(cByteBuffer & a_ByteBuffer)
2014-09-08 17:24:33 +00:00
{
HANDLE_READ(a_ByteBuffer, ReadBEUInt8, UInt8, Flags);
2014-09-08 17:24:33 +00:00
HANDLE_READ(a_ByteBuffer, ReadBEFloat, float, FlyingSpeed);
HANDLE_READ(a_ByteBuffer, ReadBEFloat, float, WalkingSpeed);
// COnvert the bitfield into individual boolean flags:
2014-09-08 17:24:33 +00:00
bool IsFlying = false, CanFly = false;
if ((Flags & 2) != 0)
{
IsFlying = true;
}
if ((Flags & 4) != 0)
{
CanFly = true;
}
m_Client->HandlePlayerAbilities(CanFly, IsFlying, FlyingSpeed, WalkingSpeed);
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::HandlePacketPlayerLook(cByteBuffer & a_ByteBuffer)
2014-09-08 17:24:33 +00:00
{
HANDLE_READ(a_ByteBuffer, ReadBEFloat, float, Yaw);
HANDLE_READ(a_ByteBuffer, ReadBEFloat, float, Pitch);
HANDLE_READ(a_ByteBuffer, ReadBool, bool, IsOnGround);
m_Client->HandlePlayerLook(Yaw, Pitch, IsOnGround);
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::HandlePacketPlayerPos(cByteBuffer & a_ByteBuffer)
2014-09-08 17:24:33 +00:00
{
HANDLE_READ(a_ByteBuffer, ReadBEDouble, double, PosX);
HANDLE_READ(a_ByteBuffer, ReadBEDouble, double, PosY);
HANDLE_READ(a_ByteBuffer, ReadBEDouble, double, PosZ);
HANDLE_READ(a_ByteBuffer, ReadBool, bool, IsOnGround);
m_Client->HandlePlayerPos(PosX, PosY, PosZ, PosY + (m_Client->GetPlayer()->IsCrouched() ? 1.54 : 1.62), IsOnGround);
2014-09-08 17:24:33 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::HandlePacketPlayerPosLook(cByteBuffer & a_ByteBuffer)
2014-09-08 17:24:33 +00:00
{
HANDLE_READ(a_ByteBuffer, ReadBEDouble, double, PosX);
HANDLE_READ(a_ByteBuffer, ReadBEDouble, double, PosY);
HANDLE_READ(a_ByteBuffer, ReadBEDouble, double, PosZ);
HANDLE_READ(a_ByteBuffer, ReadBEFloat, float, Yaw);
HANDLE_READ(a_ByteBuffer, ReadBEFloat, float, Pitch);
HANDLE_READ(a_ByteBuffer, ReadBool, bool, IsOnGround);
m_Client->HandlePlayerMoveLook(PosX, PosY, PosZ, PosY + 1.62, Yaw, Pitch, IsOnGround);
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::HandlePacketPluginMessage(cByteBuffer & a_ByteBuffer)
2014-09-08 17:24:33 +00:00
{
HANDLE_READ(a_ByteBuffer, ReadVarUTF8String, AString, Channel);
2016-02-05 21:45:45 +00:00
// If the plugin channel is recognized vanilla, handle it directly:
if (Channel.substr(0, 3) == "MC|")
2014-09-27 21:22:26 +00:00
{
HandleVanillaPluginMessage(a_ByteBuffer, Channel);
// Skip any unread data (vanilla sometimes sends garbage at the end of a packet; #1692):
if (a_ByteBuffer.GetReadableSpace() > 1)
{
LOGD("Protocol 1.8: Skipping garbage data at the end of a vanilla PluginMessage packet, %u bytes",
2015-01-03 21:39:55 +00:00
static_cast<unsigned>(a_ByteBuffer.GetReadableSpace() - 1)
);
a_ByteBuffer.SkipRead(a_ByteBuffer.GetReadableSpace() - 1);
}
return;
2014-09-27 21:22:26 +00:00
}
// Read the plugin message and relay to clienthandle:
AString Data;
VERIFY(a_ByteBuffer.ReadString(Data, a_ByteBuffer.GetReadableSpace() - 1)); // Always succeeds
2014-09-08 17:24:33 +00:00
m_Client->HandlePluginMessage(Channel, Data);
}
2020-04-07 21:23:54 +00:00
void cProtocol_1_8_0::HandlePacketResourcePackStatus(cByteBuffer & a_ByteBuffer)
{
HANDLE_READ(a_ByteBuffer, ReadVarUTF8String, AString, Hash);
HANDLE_READ(a_ByteBuffer, ReadBEUInt8, UInt8, Status);
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::HandlePacketSlotSelect(cByteBuffer & a_ByteBuffer)
2014-09-08 17:24:33 +00:00
{
HANDLE_READ(a_ByteBuffer, ReadBEInt16, Int16, SlotNum);
2014-09-08 17:24:33 +00:00
m_Client->HandleSlotSelected(SlotNum);
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::HandlePacketSpectate(cByteBuffer &a_ByteBuffer)
2016-10-12 12:38:45 +00:00
{
2017-08-25 12:43:18 +00:00
cUUID playerUUID;
2016-10-12 12:38:45 +00:00
if (!a_ByteBuffer.ReadUUID(playerUUID))
{
return;
}
m_Client->HandleSpectate(playerUUID);
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::HandlePacketSteerVehicle(cByteBuffer & a_ByteBuffer)
2014-09-08 17:24:33 +00:00
{
HANDLE_READ(a_ByteBuffer, ReadBEFloat, float, Sideways);
HANDLE_READ(a_ByteBuffer, ReadBEFloat, float, Forward);
HANDLE_READ(a_ByteBuffer, ReadBEUInt8, UInt8, Flags);
2014-09-08 17:24:33 +00:00
if ((Flags & 0x2) != 0)
{
m_Client->HandleUnmount();
}
else if ((Flags & 0x1) != 0)
{
// jump
}
else
2014-09-08 17:24:33 +00:00
{
m_Client->HandleSteerVehicle(Forward, Sideways);
}
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::HandlePacketTabComplete(cByteBuffer & a_ByteBuffer)
2014-09-08 17:24:33 +00:00
{
HANDLE_READ(a_ByteBuffer, ReadVarUTF8String, AString, Text);
HANDLE_READ(a_ByteBuffer, ReadBool, bool, HasPosition);
2014-09-08 17:24:33 +00:00
if (HasPosition)
{
HANDLE_READ(a_ByteBuffer, ReadBEInt64, Int64, Position);
}
m_Client->HandleTabCompletion(Text);
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::HandlePacketUpdateSign(cByteBuffer & a_ByteBuffer)
2014-09-08 17:24:33 +00:00
{
2014-09-11 18:06:28 +00:00
int BlockX, BlockY, BlockZ;
if (!a_ByteBuffer.ReadPosition64(BlockX, BlockY, BlockZ))
2014-09-11 18:06:28 +00:00
{
return;
}
AString Lines[4];
Json::Value root;
2014-09-11 18:06:28 +00:00
for (int i = 0; i < 4; i++)
{
HANDLE_READ(a_ByteBuffer, ReadVarUTF8String, AString, Line);
if (JsonUtils::ParseString(Line, root))
{
Lines[i] = root.asString();
}
2014-09-11 18:06:28 +00:00
}
m_Client->HandleUpdateSign(BlockX, BlockY, BlockZ, Lines[0], Lines[1], Lines[2], Lines[3]);
2014-09-08 17:24:33 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::HandlePacketUseEntity(cByteBuffer & a_ByteBuffer)
2014-09-08 17:24:33 +00:00
{
HANDLE_READ(a_ByteBuffer, ReadVarInt, UInt32, EntityID);
HANDLE_READ(a_ByteBuffer, ReadVarInt, UInt32, Type);
switch (Type)
{
case 0:
{
m_Client->HandleUseEntity(EntityID, false);
2014-09-08 17:24:33 +00:00
break;
}
case 1:
{
m_Client->HandleUseEntity(EntityID, true);
2014-09-08 17:24:33 +00:00
break;
}
case 2:
{
HANDLE_READ(a_ByteBuffer, ReadBEFloat, float, TargetX);
HANDLE_READ(a_ByteBuffer, ReadBEFloat, float, TargetY);
HANDLE_READ(a_ByteBuffer, ReadBEFloat, float, TargetZ);
// TODO: Do anything
break;
}
default:
{
ASSERT(!"Unhandled use entity type!");
return;
}
}
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::HandlePacketEnchantItem(cByteBuffer & a_ByteBuffer)
2014-09-08 17:24:33 +00:00
{
HANDLE_READ(a_ByteBuffer, ReadBEUInt8, UInt8, WindowID);
HANDLE_READ(a_ByteBuffer, ReadBEUInt8, UInt8, Enchantment);
2014-09-08 17:24:33 +00:00
m_Client->HandleEnchantItem(WindowID, Enchantment);
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::HandlePacketWindowClick(cByteBuffer & a_ByteBuffer)
2014-09-08 17:24:33 +00:00
{
HANDLE_READ(a_ByteBuffer, ReadBEUInt8, UInt8, WindowID);
HANDLE_READ(a_ByteBuffer, ReadBEInt16, Int16, SlotNum);
HANDLE_READ(a_ByteBuffer, ReadBEUInt8, UInt8, Button);
HANDLE_READ(a_ByteBuffer, ReadBEUInt16, UInt16, TransactionID);
HANDLE_READ(a_ByteBuffer, ReadBEUInt8, UInt8, Mode);
2014-09-08 17:24:33 +00:00
cItem Item;
2014-09-22 20:06:08 +00:00
ReadItem(a_ByteBuffer, Item);
2014-09-08 17:24:33 +00:00
2020-05-04 10:50:02 +00:00
/** The slot number that the client uses to indicate "outside the window". */
static const Int16 SLOT_NUM_OUTSIDE = -999;
2014-09-08 17:24:33 +00:00
// Convert Button, Mode, SlotNum and HeldItem into eClickAction:
eClickAction Action;
switch ((Mode << 8) | Button)
{
case 0x0000: Action = (SlotNum != SLOT_NUM_OUTSIDE) ? caLeftClick : caLeftClickOutside; break;
case 0x0001: Action = (SlotNum != SLOT_NUM_OUTSIDE) ? caRightClick : caRightClickOutside; break;
2014-09-08 17:24:33 +00:00
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 0x0302: Action = caMiddleClick; break;
case 0x0400: Action = (SlotNum == SLOT_NUM_OUTSIDE) ? caLeftClickOutsideHoldNothing : caDropKey; break;
case 0x0401: Action = (SlotNum == SLOT_NUM_OUTSIDE) ? caRightClickOutsideHoldNothing : caCtrlDropKey; break;
case 0x0500: Action = (SlotNum == SLOT_NUM_OUTSIDE) ? caLeftPaintBegin : caUnknown; break;
case 0x0501: Action = (SlotNum != SLOT_NUM_OUTSIDE) ? caLeftPaintProgress : caUnknown; break;
case 0x0502: Action = (SlotNum == SLOT_NUM_OUTSIDE) ? caLeftPaintEnd : caUnknown; break;
case 0x0504: Action = (SlotNum == SLOT_NUM_OUTSIDE) ? caRightPaintBegin : caUnknown; break;
case 0x0505: Action = (SlotNum != SLOT_NUM_OUTSIDE) ? caRightPaintProgress : caUnknown; break;
case 0x0506: Action = (SlotNum == SLOT_NUM_OUTSIDE) ? caRightPaintEnd : caUnknown; break;
2017-07-13 13:43:48 +00:00
case 0x0508: Action = (SlotNum == SLOT_NUM_OUTSIDE) ? caMiddlePaintBegin : caUnknown; break;
case 0x0509: Action = (SlotNum != SLOT_NUM_OUTSIDE) ? caMiddlePaintProgress : caUnknown; break;
case 0x050a: Action = (SlotNum == SLOT_NUM_OUTSIDE) ? caMiddlePaintEnd : caUnknown; break;
2014-09-08 17:24:33 +00:00
case 0x0600: Action = caDblClick; break;
default:
{
LOGWARNING("Unhandled window click mode / button combination: %d (0x%x)", (Mode << 8) | Button, (Mode << 8) | Button);
Action = caUnknown;
break;
}
}
m_Client->HandleWindowClick(WindowID, SlotNum, Action, Item);
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::HandlePacketWindowClose(cByteBuffer & a_ByteBuffer)
2014-09-08 17:24:33 +00:00
{
HANDLE_READ(a_ByteBuffer, ReadBEUInt8, UInt8, WindowID);
2014-09-08 17:24:33 +00:00
m_Client->HandleWindowClose(WindowID);
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::HandleVanillaPluginMessage(cByteBuffer & a_ByteBuffer, const AString & a_Channel)
{
if (a_Channel == "MC|AdvCdm")
{
2019-08-11 09:39:43 +00:00
HANDLE_READ(a_ByteBuffer, ReadBEUInt8, UInt8, Mode);
switch (Mode)
{
case 0x00:
{
HANDLE_READ(a_ByteBuffer, ReadBEInt32, Int32, BlockX);
HANDLE_READ(a_ByteBuffer, ReadBEInt32, Int32, BlockY);
HANDLE_READ(a_ByteBuffer, ReadBEInt32, Int32, BlockZ);
HANDLE_READ(a_ByteBuffer, ReadVarUTF8String, AString, Command);
m_Client->HandleCommandBlockBlockChange(BlockX, BlockY, BlockZ, Command);
break;
}
default:
{
m_Client->SendChat(Printf("Failure setting command block command; unhandled mode %u (0x%02x)", Mode, Mode), mtFailure);
LOG("Unhandled MC|AdvCdm packet mode.");
return;
}
} // switch (Mode)
return;
}
else if (a_Channel == "MC|Brand")
{
HANDLE_READ(a_ByteBuffer, ReadVarUTF8String, AString, Brand);
m_Client->SetClientBrand(Brand);
// Send back our brand, including the length:
SendPluginMessage("MC|Brand", "\x08""Cuberite");
return;
}
else if (a_Channel == "MC|Beacon")
{
HANDLE_READ(a_ByteBuffer, ReadBEInt32, Int32, Effect1);
HANDLE_READ(a_ByteBuffer, ReadBEInt32, Int32, Effect2);
m_Client->HandleBeaconSelection(Effect1, Effect2);
return;
}
else if (a_Channel == "MC|ItemName")
{
HANDLE_READ(a_ByteBuffer, ReadVarUTF8String, AString, ItemName);
m_Client->HandleAnvilItemName(ItemName);
return;
}
else if (a_Channel == "MC|TrSel")
{
HANDLE_READ(a_ByteBuffer, ReadBEInt32, Int32, SlotNum);
m_Client->HandleNPCTrade(SlotNum);
return;
}
LOG("Unhandled vanilla plugin channel: \"%s\".", a_Channel.c_str());
2016-02-05 21:45:45 +00:00
// Read the payload and send it through to the clienthandle:
AString Message;
VERIFY(a_ByteBuffer.ReadString(Message, a_ByteBuffer.GetReadableSpace() - 1));
m_Client->HandlePluginMessage(a_Channel, Message);
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendData(const char * a_Data, size_t a_Size)
{
2014-09-08 17:24:33 +00:00
if (m_IsEncrypted)
{
2014-09-08 17:24:33 +00:00
Byte Encrypted[8192]; // Larger buffer, we may be sending lots of data (chunks)
while (a_Size > 0)
{
size_t NumBytes = (a_Size > sizeof(Encrypted)) ? sizeof(Encrypted) : a_Size;
m_Encryptor.ProcessData(Encrypted, reinterpret_cast<const Byte *>(a_Data), NumBytes);
m_Client->SendData(reinterpret_cast<const char *>(Encrypted), NumBytes);
2014-09-08 17:24:33 +00:00
a_Size -= NumBytes;
a_Data += NumBytes;
}
}
2014-09-08 17:24:33 +00:00
else
{
2014-09-08 17:24:33 +00:00
m_Client->SendData(a_Data, a_Size);
}
}
2016-12-15 19:21:43 +00:00
bool cProtocol_1_8_0::ReadItem(cByteBuffer & a_ByteBuffer, cItem & a_Item, size_t a_KeepRemainingBytes)
2014-09-08 15:02:54 +00:00
{
HANDLE_PACKET_READ(a_ByteBuffer, ReadBEInt16, Int16, ItemType);
2014-09-08 15:02:54 +00:00
if (ItemType == -1)
{
// The item is empty, no more data follows
a_Item.Empty();
return true;
}
a_Item.m_ItemType = ItemType;
2016-02-05 21:45:45 +00:00
HANDLE_PACKET_READ(a_ByteBuffer, ReadBEInt8, Int8, ItemCount);
HANDLE_PACKET_READ(a_ByteBuffer, ReadBEInt16, Int16, ItemDamage);
2014-09-08 15:02:54 +00:00
a_Item.m_ItemCount = ItemCount;
a_Item.m_ItemDamage = ItemDamage;
if (ItemCount <= 0)
{
a_Item.Empty();
}
2014-09-22 19:18:13 +00:00
AString Metadata;
if (!a_ByteBuffer.ReadString(Metadata, a_ByteBuffer.GetReadableSpace() - a_KeepRemainingBytes - 1) || (Metadata.size() == 0) || (Metadata[0] == 0))
2014-09-08 15:02:54 +00:00
{
// No metadata
return true;
}
2014-09-12 00:42:04 +00:00
2014-09-08 17:24:33 +00:00
ParseItemMetadata(a_Item, Metadata);
2014-09-08 15:02:54 +00:00
return true;
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::ParseItemMetadata(cItem & a_Item, const AString & a_Metadata)
2014-09-08 15:02:54 +00:00
{
2014-09-08 17:24:33 +00:00
// Parse into NBT:
cParsedNBT NBT(a_Metadata.data(), a_Metadata.size());
if (!NBT.IsValid())
2014-09-08 15:02:54 +00:00
{
2014-09-08 17:24:33 +00:00
AString HexDump;
CreateHexDump(HexDump, a_Metadata.data(), std::max<size_t>(a_Metadata.size(), 1024), 16);
LOGWARNING("Cannot parse NBT item metadata: %s at (%zu / %zu bytes)\n%s",
NBT.GetErrorCode().message().c_str(), NBT.GetErrorPos(), a_Metadata.size(), HexDump.c_str()
);
2014-09-08 15:02:54 +00:00
return;
}
2016-02-05 21:45:45 +00:00
2014-09-08 17:24:33 +00:00
// Load enchantments and custom display names from the NBT data:
for (int tag = NBT.GetFirstChild(NBT.GetRoot()); tag >= 0; tag = NBT.GetNextSibling(tag))
2014-09-04 01:22:35 +00:00
{
2014-09-08 17:24:33 +00:00
AString TagName = NBT.GetName(tag);
switch (NBT.GetType(tag))
2014-09-04 01:22:35 +00:00
{
2014-09-08 17:24:33 +00:00
case TAG_List:
{
if ((TagName == "ench") || (TagName == "StoredEnchantments")) // Enchantments tags
{
EnchantmentSerializer::ParseFromNBT(a_Item.m_Enchantments, NBT, tag);
}
break;
}
case TAG_Compound:
{
if (TagName == "display") // Custom name and lore tag
{
for (int displaytag = NBT.GetFirstChild(tag); displaytag >= 0; displaytag = NBT.GetNextSibling(displaytag))
{
if ((NBT.GetType(displaytag) == TAG_String) && (NBT.GetName(displaytag) == "Name")) // Custon name tag
{
a_Item.m_CustomName = NBT.GetString(displaytag);
}
else if ((NBT.GetType(displaytag) == TAG_List) && (NBT.GetName(displaytag) == "Lore")) // Lore tag
{
for (int loretag = NBT.GetFirstChild(displaytag); loretag >= 0; loretag = NBT.GetNextSibling(loretag)) // Loop through array of strings
{
a_Item.m_LoreTable.push_back(NBT.GetString(loretag));
2014-09-08 17:24:33 +00:00
}
}
else if ((NBT.GetType(displaytag) == TAG_Int) && (NBT.GetName(displaytag) == "color"))
{
a_Item.m_ItemColor.m_Color = static_cast<unsigned int>(NBT.GetInt(displaytag));
}
2014-09-08 17:24:33 +00:00
}
}
else if ((TagName == "Fireworks") || (TagName == "Explosion"))
{
cFireworkItem::ParseFromNBT(a_Item.m_FireworkItem, NBT, tag, static_cast<ENUM_ITEM_TYPE>(a_Item.m_ItemType));
2014-09-08 17:24:33 +00:00
}
break;
}
case TAG_Int:
{
if (TagName == "RepairCost")
{
a_Item.m_RepairCost = NBT.GetInt(tag);
}
}
default: LOGD("Unimplemented NBT data when parsing!"); break;
2014-09-04 01:22:35 +00:00
}
}
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::StartEncryption(const Byte * a_Key)
{
2014-09-08 17:24:33 +00:00
m_Encryptor.Init(a_Key, a_Key);
m_Decryptor.Init(a_Key, a_Key);
m_IsEncrypted = true;
2016-02-05 21:45:45 +00:00
2014-09-08 17:24:33 +00:00
// Prepare the m_AuthServerID:
cSha1Checksum Checksum;
cServer * Server = cRoot::Get()->GetServer();
2014-09-08 17:24:33 +00:00
const AString & ServerID = Server->GetServerID();
Checksum.Update(reinterpret_cast<const Byte *>(ServerID.c_str()), ServerID.length());
2014-09-08 17:24:33 +00:00
Checksum.Update(a_Key, 16);
Checksum.Update(reinterpret_cast<const Byte *>(Server->GetPublicKeyDER().data()), Server->GetPublicKeyDER().size());
2014-09-08 17:24:33 +00:00
Byte Digest[20];
Checksum.Finalize(Digest);
cSha1Checksum::DigestToJava(Digest, m_AuthServerID);
}
2016-12-15 19:21:43 +00:00
eBlockFace cProtocol_1_8_0::FaceIntToBlockFace(Int8 a_BlockFace)
{
// Normalize the blockface values returned from the protocol
// Anything known gets mapped 1:1, everything else returns BLOCK_FACE_NONE
switch (a_BlockFace)
{
case BLOCK_FACE_XM: return BLOCK_FACE_XM;
case BLOCK_FACE_XP: return BLOCK_FACE_XP;
case BLOCK_FACE_YM: return BLOCK_FACE_YM;
case BLOCK_FACE_YP: return BLOCK_FACE_YP;
case BLOCK_FACE_ZM: return BLOCK_FACE_ZM;
case BLOCK_FACE_ZP: return BLOCK_FACE_ZP;
default: return BLOCK_FACE_NONE;
}
}
2014-09-08 17:24:33 +00:00
////////////////////////////////////////////////////////////////////////////////
2016-12-15 19:21:43 +00:00
// cProtocol_1_8_0::cPacketizer:
2014-09-08 17:24:33 +00:00
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::SendPacket(cPacketizer & a_Pkt)
2014-09-04 01:22:35 +00:00
{
UInt32 PacketLen = static_cast<UInt32>(m_OutPacketBuffer.GetUsedSpace());
2014-09-12 00:42:04 +00:00
AString PacketData, CompressedPacket;
m_OutPacketBuffer.ReadAll(PacketData);
m_OutPacketBuffer.CommitRead();
2014-09-08 17:24:33 +00:00
if ((m_State == 3) && (PacketLen >= 256))
2014-09-12 00:42:04 +00:00
{
// Compress the packet payload:
2016-12-15 19:21:43 +00:00
if (!cProtocol_1_8_0::CompressPacket(PacketData, CompressedPacket))
2014-09-12 00:42:04 +00:00
{
return;
}
}
else if (m_State == 3)
2014-09-04 01:22:35 +00:00
{
// The packet is not compressed, indicate this in the packet header:
m_OutPacketLenBuffer.WriteVarInt32(PacketLen + 1);
m_OutPacketLenBuffer.WriteVarInt32(0);
2014-09-12 00:42:04 +00:00
AString LengthData;
m_OutPacketLenBuffer.ReadAll(LengthData);
SendData(LengthData.data(), LengthData.size());
2014-09-04 01:22:35 +00:00
}
2014-09-08 17:24:33 +00:00
else
2014-09-04 01:22:35 +00:00
{
// Compression doesn't apply to this state, send raw data:
m_OutPacketLenBuffer.WriteVarInt32(PacketLen);
2014-09-12 00:42:04 +00:00
AString LengthData;
m_OutPacketLenBuffer.ReadAll(LengthData);
SendData(LengthData.data(), LengthData.size());
2014-09-08 17:24:33 +00:00
}
2014-09-12 00:42:04 +00:00
// Send the packet's payload, either direct or compressed:
2014-09-12 00:42:04 +00:00
if (CompressedPacket.empty())
{
m_OutPacketLenBuffer.CommitRead();
SendData(PacketData.data(), PacketData.size());
2014-09-12 00:42:04 +00:00
}
else
{
SendData(CompressedPacket.data(), CompressedPacket.size());
2014-09-12 00:42:04 +00:00
}
2014-09-08 17:24:33 +00:00
// Log the comm into logfile:
if (g_ShouldLogCommOut && m_CommLogFile.IsOpen())
2014-09-08 17:24:33 +00:00
{
AString Hex;
2014-09-12 00:42:04 +00:00
ASSERT(PacketData.size() > 0);
CreateHexDump(Hex, PacketData.data(), PacketData.size(), 16);
m_CommLogFile.Printf("Outgoing packet: type %s (translated to 0x%02x), length %u (0x%04x), state %d. Payload (incl. type):\n%s\n",
cPacketizer::PacketTypeToStr(a_Pkt.GetPacketType()), GetPacketID(a_Pkt.GetPacketType()),
PacketLen, PacketLen, m_State, Hex
);
/*
// Useful for debugging a new protocol:
LOGD("Outgoing packet: type %s (translated to 0x%02x), length %u (0x%04x), state %d. Payload (incl. type):\n%s\n",
cPacketizer::PacketTypeToStr(a_Pkt.GetPacketType()), GetPacketID(a_Pkt.GetPacketType()),
PacketLen, PacketLen, m_State, Hex
2014-09-08 17:24:33 +00:00
);
//*/
2014-09-04 01:22:35 +00:00
}
/*
// Useful for debugging a new protocol:
std::this_thread::sleep_for(std::chrono::milliseconds(100));
*/
2014-09-04 01:22:35 +00:00
}
void cProtocol_1_8_0::SendEntitySpawn(const cEntity & a_Entity, const UInt8 a_ObjectType, const Int32 a_ObjectData)
{
ASSERT(m_State == 3); // In game mode?
{
cPacketizer Pkt(*this, pktSpawnObject);
Pkt.WriteVarInt32(a_Entity.GetUniqueID());
Pkt.WriteBEUInt8(a_ObjectType);
Pkt.WriteFPInt(a_Entity.GetPosX()); // Position appears to be ignored...
Pkt.WriteFPInt(a_Entity.GetPosY());
Pkt.WriteFPInt(a_Entity.GetPosY());
Pkt.WriteByteAngle(a_Entity.GetPitch());
Pkt.WriteByteAngle(a_Entity.GetYaw());
Pkt.WriteBEInt32(a_ObjectData);
if (a_ObjectData != 0)
{
Pkt.WriteBEInt16(static_cast<Int16>(a_Entity.GetSpeedX() * 400));
Pkt.WriteBEInt16(static_cast<Int16>(a_Entity.GetSpeedY() * 400));
Pkt.WriteBEInt16(static_cast<Int16>(a_Entity.GetSpeedZ() * 400));
}
}
// Otherwise 1.8 clients don't show the entity
SendEntityTeleport(a_Entity);
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::WriteItem(cPacketizer & a_Pkt, const cItem & a_Item)
2014-09-04 01:22:35 +00:00
{
2014-09-08 17:24:33 +00:00
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;
}
2016-02-05 21:45:45 +00:00
2014-09-08 17:24:33 +00:00
if (a_Item.IsEmpty())
{
a_Pkt.WriteBEInt16(-1);
2014-09-08 17:24:33 +00:00
return;
}
2016-02-05 21:45:45 +00:00
a_Pkt.WriteBEInt16(ItemType);
a_Pkt.WriteBEInt8(a_Item.m_ItemCount);
a_Pkt.WriteBEInt16(a_Item.m_ItemDamage);
2016-02-05 21:45:45 +00:00
if (a_Item.m_Enchantments.IsEmpty() && a_Item.IsBothNameAndLoreEmpty() && (a_Item.m_ItemType != E_ITEM_FIREWORK_ROCKET) && (a_Item.m_ItemType != E_ITEM_FIREWORK_STAR) && !a_Item.m_ItemColor.IsValid())
2014-09-08 17:24:33 +00:00
{
a_Pkt.WriteBEInt8(0);
2014-09-08 17:24:33 +00:00
return;
}
2014-09-04 01:22:35 +00:00
2014-09-08 17:24:33 +00:00
// Send the enchantments and custom names:
cFastNBTWriter Writer;
if (a_Item.m_RepairCost != 0)
2014-09-04 01:22:35 +00:00
{
2014-09-08 17:24:33 +00:00
Writer.AddInt("RepairCost", a_Item.m_RepairCost);
}
if (!a_Item.m_Enchantments.IsEmpty())
{
const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench";
EnchantmentSerializer::WriteToNBTCompound(a_Item.m_Enchantments, Writer, TagName);
}
if (!a_Item.IsBothNameAndLoreEmpty() || a_Item.m_ItemColor.IsValid())
2014-09-08 17:24:33 +00:00
{
Writer.BeginCompound("display");
if (a_Item.m_ItemColor.IsValid())
{
Writer.AddInt("color", static_cast<Int32>(a_Item.m_ItemColor.m_Color));
}
2014-09-08 17:24:33 +00:00
if (!a_Item.IsCustomNameEmpty())
{
Writer.AddString("Name", a_Item.m_CustomName.c_str());
}
if (!a_Item.IsLoreEmpty())
{
Writer.BeginList("Lore", TAG_String);
for (const auto & Line : a_Item.m_LoreTable)
2014-09-08 17:24:33 +00:00
{
Writer.AddString("", Line);
2014-09-08 17:24:33 +00:00
}
Writer.EndList();
}
Writer.EndCompound();
}
if ((a_Item.m_ItemType == E_ITEM_FIREWORK_ROCKET) || (a_Item.m_ItemType == E_ITEM_FIREWORK_STAR))
{
cFireworkItem::WriteToNBTCompound(a_Item.m_FireworkItem, Writer, static_cast<ENUM_ITEM_TYPE>(a_Item.m_ItemType));
2014-09-04 01:22:35 +00:00
}
2014-09-08 17:24:33 +00:00
Writer.Finish();
AString Result = Writer.GetResult();
if (Result.size() == 0)
{
a_Pkt.WriteBEInt8(0);
2014-09-08 17:24:33 +00:00
return;
}
a_Pkt.WriteBuf(Result.data(), Result.size());
2014-09-04 01:22:35 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::WriteBlockEntity(cPacketizer & a_Pkt, const cBlockEntity & a_BlockEntity)
2014-09-04 01:22:35 +00:00
{
2014-09-08 17:24:33 +00:00
cFastNBTWriter Writer;
2014-09-04 01:22:35 +00:00
2014-09-08 17:24:33 +00:00
switch (a_BlockEntity.GetBlockType())
2014-09-04 01:22:35 +00:00
{
2014-09-08 17:24:33 +00:00
case E_BLOCK_BEACON:
2014-09-04 01:22:35 +00:00
{
auto & BeaconEntity = static_cast<const cBeaconEntity &>(a_BlockEntity);
Writer.AddInt("x", BeaconEntity.GetPosX());
Writer.AddInt("y", BeaconEntity.GetPosY());
Writer.AddInt("z", BeaconEntity.GetPosZ());
Writer.AddInt("Primary", BeaconEntity.GetPrimaryEffect());
2014-09-08 17:24:33 +00:00
Writer.AddInt("Secondary", BeaconEntity.GetSecondaryEffect());
Writer.AddInt("Levels", BeaconEntity.GetBeaconLevel());
2014-09-08 17:24:33 +00:00
Writer.AddString("id", "Beacon"); // "Tile Entity ID" - MC wiki; vanilla server always seems to send this though
2014-09-04 01:22:35 +00:00
break;
}
2014-09-08 17:24:33 +00:00
case E_BLOCK_COMMAND_BLOCK:
2014-09-04 01:22:35 +00:00
{
auto & CommandBlockEntity = static_cast<const cCommandBlockEntity &>(a_BlockEntity);
2014-09-08 17:24:33 +00:00
Writer.AddByte("TrackOutput", 1); // Neither I nor the MC wiki has any idea about this
Writer.AddInt("SuccessCount", CommandBlockEntity.GetResult());
Writer.AddInt("x", CommandBlockEntity.GetPosX());
Writer.AddInt("y", CommandBlockEntity.GetPosY());
Writer.AddInt("z", CommandBlockEntity.GetPosZ());
Writer.AddString("Command", CommandBlockEntity.GetCommand().c_str());
// You can set custom names for windows in Vanilla
// For a command block, this would be the 'name' prepended to anything it outputs into global chat
// MCS doesn't have this, so just leave it @ '@'. (geddit?)
Writer.AddString("CustomName", "@");
Writer.AddString("id", "Control"); // "Tile Entity ID" - MC wiki; vanilla server always seems to send this though
if (!CommandBlockEntity.GetLastOutput().empty())
{
Writer.AddString("LastOutput", Printf("{\"text\":\"%s\"}", CommandBlockEntity.GetLastOutput().c_str()));
2014-09-08 17:24:33 +00:00
}
2014-09-04 01:22:35 +00:00
break;
}
2014-09-08 17:24:33 +00:00
case E_BLOCK_HEAD:
2014-09-04 01:22:35 +00:00
{
auto & MobHeadEntity = static_cast<const cMobHeadEntity &>(a_BlockEntity);
2014-09-08 17:24:33 +00:00
Writer.AddInt("x", MobHeadEntity.GetPosX());
Writer.AddInt("y", MobHeadEntity.GetPosY());
Writer.AddInt("z", MobHeadEntity.GetPosZ());
Writer.AddByte("SkullType", MobHeadEntity.GetType() & 0xFF);
Writer.AddByte("Rot", MobHeadEntity.GetRotation() & 0xFF);
Writer.AddString("id", "Skull"); // "Tile Entity ID" - MC wiki; vanilla server always seems to send this though
2017-08-24 09:19:40 +00:00
// The new Block Entity format for a Mob Head. See: https://minecraft.gamepedia.com/Head#Block_entity
Writer.BeginCompound("Owner");
2017-08-25 12:43:18 +00:00
Writer.AddString("Id", MobHeadEntity.GetOwnerUUID().ToShortString());
Writer.AddString("Name", MobHeadEntity.GetOwnerName());
Writer.BeginCompound("Properties");
Writer.BeginList("textures", TAG_Compound);
Writer.BeginCompound("");
Writer.AddString("Signature", MobHeadEntity.GetOwnerTextureSignature());
Writer.AddString("Value", MobHeadEntity.GetOwnerTexture());
Writer.EndCompound();
Writer.EndList();
Writer.EndCompound();
Writer.EndCompound();
2014-09-04 01:22:35 +00:00
break;
}
2014-09-08 17:24:33 +00:00
case E_BLOCK_FLOWER_POT:
2014-09-04 01:22:35 +00:00
{
auto & FlowerPotEntity = static_cast<const cFlowerPotEntity &>(a_BlockEntity);
2014-09-08 17:24:33 +00:00
Writer.AddInt("x", FlowerPotEntity.GetPosX());
Writer.AddInt("y", FlowerPotEntity.GetPosY());
Writer.AddInt("z", FlowerPotEntity.GetPosZ());
Writer.AddInt("Item", static_cast<Int32>(FlowerPotEntity.GetItem().m_ItemType));
Writer.AddInt("Data", static_cast<Int32>(FlowerPotEntity.GetItem().m_ItemDamage));
2014-09-08 17:24:33 +00:00
Writer.AddString("id", "FlowerPot"); // "Tile Entity ID" - MC wiki; vanilla server always seems to send this though
break;
2014-09-04 01:22:35 +00:00
}
2014-11-18 14:33:41 +00:00
case E_BLOCK_MOB_SPAWNER:
{
auto & MobSpawnerEntity = static_cast<const cMobSpawnerEntity &>(a_BlockEntity);
2014-11-18 14:33:41 +00:00
Writer.AddInt("x", MobSpawnerEntity.GetPosX());
Writer.AddInt("y", MobSpawnerEntity.GetPosY());
Writer.AddInt("z", MobSpawnerEntity.GetPosZ());
Writer.AddString("EntityId", cMonster::MobTypeToVanillaName(MobSpawnerEntity.GetEntity()));
2014-11-18 14:33:41 +00:00
Writer.AddShort("Delay", MobSpawnerEntity.GetSpawnDelay());
Writer.AddString("id", "MobSpawner");
break;
}
default:
{
break;
}
2014-09-04 01:22:35 +00:00
}
2014-09-08 17:24:33 +00:00
Writer.Finish();
a_Pkt.WriteBuf(Writer.GetResult().data(), Writer.GetResult().size());
2014-09-04 01:22:35 +00:00
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::WriteEntityMetadata(cPacketizer & a_Pkt, const cEntity & a_Entity)
2014-09-04 17:03:21 +00:00
{
2014-09-08 17:24:33 +00:00
// Common metadata:
Byte Flags = 0;
if (a_Entity.IsOnFire())
{
Flags |= 0x01;
}
if (a_Entity.IsCrouched())
{
Flags |= 0x02;
}
if (a_Entity.IsSprinting())
{
Flags |= 0x08;
}
if (a_Entity.IsRclking())
{
Flags |= 0x10;
}
if (a_Entity.IsInvisible())
{
Flags |= 0x20;
}
a_Pkt.WriteBEUInt8(0); // Byte(0) + index 0
a_Pkt.WriteBEUInt8(Flags);
2014-09-08 17:24:33 +00:00
switch (a_Entity.GetEntityType())
2014-09-08 15:02:54 +00:00
{
case cEntity::etPlayer:
{
auto & Player = static_cast<const cPlayer &>(a_Entity);
// Player health (not handled since players aren't monsters)
a_Pkt.WriteBEUInt8(0x66);
a_Pkt.WriteBEFloat(static_cast<float>(Player.GetHealth()));
// Skin flags
a_Pkt.WriteBEUInt8(0x0A);
a_Pkt.WriteBEUInt8(static_cast<UInt8>(Player.GetSkinParts()));
break;
}
2014-09-08 17:24:33 +00:00
case cEntity::etPickup:
{
a_Pkt.WriteBEUInt8((5 << 5) | 10); // Slot(5) + index 10
WriteItem(a_Pkt, static_cast<const cPickup &>(a_Entity).GetItem());
2014-09-08 17:24:33 +00:00
break;
}
case cEntity::etMinecart:
{
a_Pkt.WriteBEUInt8(0x51);
2014-09-08 17:24:33 +00:00
// The following expression makes Minecarts shake more with less health or higher damage taken
// It gets half the maximum health, and takes it away from the current health minus the half health:
/*
Health: 5 | 3 - (5 - 3) = 1 (shake power)
Health: 3 | 3 - (3 - 3) = 3
Health: 1 | 3 - (1 - 3) = 5
*/
auto & Minecart = static_cast<const cMinecart &>(a_Entity);
a_Pkt.WriteBEInt32(static_cast<Int32>((((a_Entity.GetMaxHealth() / 2) - (a_Entity.GetHealth() - (a_Entity.GetMaxHealth() / 2))) * Minecart.LastDamage()) * 4));
a_Pkt.WriteBEUInt8(0x52);
a_Pkt.WriteBEInt32(1); // Shaking direction, doesn't seem to affect anything
a_Pkt.WriteBEUInt8(0x73);
a_Pkt.WriteBEFloat(static_cast<float>(Minecart.LastDamage() + 10)); // Damage taken / shake effect multiplyer
2016-02-05 21:45:45 +00:00
if (Minecart.GetPayload() == cMinecart::mpNone)
2014-09-08 17:24:33 +00:00
{
auto & RideableMinecart = static_cast<const cRideableMinecart &>(Minecart);
2014-09-08 17:24:33 +00:00
const cItem & MinecartContent = RideableMinecart.GetContent();
if (!MinecartContent.IsEmpty())
{
a_Pkt.WriteBEUInt8(0x54);
2014-09-08 17:24:33 +00:00
int Content = MinecartContent.m_ItemType;
Content |= MinecartContent.m_ItemDamage << 8;
a_Pkt.WriteBEInt32(Content);
a_Pkt.WriteBEUInt8(0x55);
a_Pkt.WriteBEInt32(RideableMinecart.GetBlockHeight());
a_Pkt.WriteBEUInt8(0x56);
a_Pkt.WriteBEUInt8(1);
2014-09-08 17:24:33 +00:00
}
}
else if (Minecart.GetPayload() == cMinecart::mpFurnace)
2014-09-08 17:24:33 +00:00
{
a_Pkt.WriteBEUInt8(0x10);
a_Pkt.WriteBEUInt8(static_cast<const cMinecartWithFurnace &>(Minecart).IsFueled() ? 1 : 0);
2014-09-08 17:24:33 +00:00
}
break;
} // case etMinecart
2014-09-08 17:24:33 +00:00
case cEntity::etProjectile:
{
auto & Projectile = static_cast<const cProjectileEntity &>(a_Entity);
2014-09-08 17:24:33 +00:00
switch (Projectile.GetProjectileKind())
{
case cProjectileEntity::pkArrow:
{
a_Pkt.WriteBEUInt8(0x10);
a_Pkt.WriteBEUInt8(static_cast<const cArrowEntity &>(Projectile).IsCritical() ? 1 : 0);
2014-09-08 17:24:33 +00:00
break;
}
case cProjectileEntity::pkFirework:
{
a_Pkt.WriteBEUInt8(0xa8);
WriteItem(a_Pkt, static_cast<const cFireworkEntity &>(Projectile).GetItem());
break;
}
default:
{
2014-09-08 17:24:33 +00:00
break;
}
}
break;
} // case etProjectile
2014-09-08 17:24:33 +00:00
case cEntity::etMonster:
{
WriteMobMetadata(a_Pkt, static_cast<const cMonster &>(a_Entity));
2014-09-08 17:24:33 +00:00
break;
}
2014-09-08 17:24:33 +00:00
case cEntity::etItemFrame:
{
auto & Frame = static_cast<const cItemFrame &>(a_Entity);
a_Pkt.WriteBEUInt8(0xa8);
WriteItem(a_Pkt, Frame.GetItem());
a_Pkt.WriteBEUInt8(0x09);
a_Pkt.WriteBEUInt8(Frame.GetItemRotation());
break;
} // case etItemFrame
default:
{
2014-09-08 17:24:33 +00:00
break;
}
2014-09-08 15:02:54 +00:00
}
}
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_Mob)
2014-09-08 15:02:54 +00:00
{
// Living Enitiy Metadata
if (a_Mob.HasCustomName())
{
a_Pkt.WriteBEUInt8(0x82);
a_Pkt.WriteString(a_Mob.GetCustomName());
a_Pkt.WriteBEUInt8(0x03);
a_Pkt.WriteBool(a_Mob.IsCustomNameAlwaysVisible());
}
a_Pkt.WriteBEUInt8(0x66);
a_Pkt.WriteBEFloat(static_cast<float>(a_Mob.GetHealth()));
2014-09-08 17:24:33 +00:00
switch (a_Mob.GetMobType())
2014-09-08 15:02:54 +00:00
{
case mtBat:
2014-09-08 17:24:33 +00:00
{
auto & Bat = static_cast<const cBat &>(a_Mob);
a_Pkt.WriteBEUInt8(0x10);
a_Pkt.WriteBEUInt8(Bat.IsHanging() ? 1 : 0);
2014-09-08 17:24:33 +00:00
break;
} // case mtBat
case mtChicken:
{
auto & Chicken = static_cast<const cChicken &>(a_Mob);
a_Pkt.WriteBEUInt8(0x0c);
a_Pkt.WriteBEInt8(Chicken.IsBaby() ? -1 : (Chicken.IsInLoveCooldown() ? 1 : 0));
break;
} // case mtChicken
case mtCow:
{
auto & Cow = static_cast<const cCow &>(a_Mob);
a_Pkt.WriteBEUInt8(0x0c);
a_Pkt.WriteBEInt8(Cow.IsBaby() ? -1 : (Cow.IsInLoveCooldown() ? 1 : 0));
break;
} // case mtCow
case mtCreeper:
2014-09-08 17:24:33 +00:00
{
auto & Creeper = static_cast<const cCreeper &>(a_Mob);
a_Pkt.WriteBEUInt8(0x10);
a_Pkt.WriteBEUInt8(Creeper.IsBlowing() ? 1 : 255);
a_Pkt.WriteBEUInt8(0x11);
a_Pkt.WriteBEUInt8(Creeper.IsCharged() ? 1 : 0);
2014-09-08 17:24:33 +00:00
break;
} // case mtCreeper
case mtEnderman:
2014-09-08 17:24:33 +00:00
{
auto & Enderman = static_cast<const cEnderman &>(a_Mob);
a_Pkt.WriteBEUInt8(0x30);
a_Pkt.WriteBEInt16(static_cast<Byte>(Enderman.GetCarriedBlock()));
a_Pkt.WriteBEUInt8(0x11);
a_Pkt.WriteBEUInt8(static_cast<Byte>(Enderman.GetCarriedMeta()));
a_Pkt.WriteBEUInt8(0x12);
a_Pkt.WriteBEUInt8(Enderman.IsScreaming() ? 1 : 0);
2014-09-08 17:24:33 +00:00
break;
} // case mtEnderman
case mtGhast:
2014-09-08 17:24:33 +00:00
{
auto & Ghast = static_cast<const cGhast &>(a_Mob);
a_Pkt.WriteBEUInt8(0x10);
a_Pkt.WriteBEUInt8(Ghast.IsCharging());
2014-09-08 17:24:33 +00:00
break;
} // case mtGhast
case mtHorse:
2014-09-08 17:24:33 +00:00
{
auto & Horse = static_cast<const cHorse &>(a_Mob);
int Flags = 0;
if (Horse.IsTame())
2014-09-08 17:24:33 +00:00
{
Flags |= 0x02;
2014-09-08 17:24:33 +00:00
}
if (Horse.IsSaddled())
2014-09-08 17:24:33 +00:00
{
Flags |= 0x04;
2014-09-08 17:24:33 +00:00
}
if (Horse.IsChested())
2014-09-08 17:24:33 +00:00
{
Flags |= 0x08;
2014-09-08 17:24:33 +00:00
}
if (Horse.IsEating())
{
Flags |= 0x20;
}
if (Horse.IsRearing())
{
Flags |= 0x40;
}
if (Horse.IsMthOpen())
{
Flags |= 0x80;
}
a_Pkt.WriteBEUInt8(0x50); // Int at index 16
a_Pkt.WriteBEInt32(Flags);
a_Pkt.WriteBEUInt8(0x13); // Byte at index 19
a_Pkt.WriteBEUInt8(static_cast<UInt8>(Horse.GetHorseType()));
a_Pkt.WriteBEUInt8(0x54); // Int at index 20
int Appearance = 0;
Appearance = Horse.GetHorseColor();
Appearance |= Horse.GetHorseStyle() << 8;
a_Pkt.WriteBEInt32(Appearance);
a_Pkt.WriteBEUInt8(0x56); // Int at index 22
a_Pkt.WriteBEInt32(Horse.GetHorseArmour());
a_Pkt.WriteBEUInt8(0x0c);
2015-11-29 18:13:31 +00:00
a_Pkt.WriteBEInt8(Horse.IsBaby() ? -1 : (Horse.IsInLoveCooldown() ? 1 : 0));
2014-09-08 17:24:33 +00:00
break;
} // case mtHorse
case mtMagmaCube:
{
auto & MagmaCube = static_cast<const cMagmaCube &>(a_Mob);
a_Pkt.WriteBEUInt8(0x10);
a_Pkt.WriteBEUInt8(static_cast<UInt8>(MagmaCube.GetSize()));
break;
} // case mtMagmaCube
case mtOcelot:
{
auto & Ocelot = static_cast<const cOcelot &>(a_Mob);
a_Pkt.WriteBEUInt8(0x0c);
2015-11-29 18:13:31 +00:00
a_Pkt.WriteBEInt8(Ocelot.IsBaby() ? -1 : (Ocelot.IsInLoveCooldown() ? 1 : 0));
break;
} // case mtOcelot
case mtPig:
{
auto & Pig = static_cast<const cPig &>(a_Mob);
a_Pkt.WriteBEUInt8(0x0c);
2015-11-29 18:13:31 +00:00
a_Pkt.WriteBEInt8(Pig.IsBaby() ? -1 : (Pig.IsInLoveCooldown() ? 1 : 0));
a_Pkt.WriteBEUInt8(0x10);
a_Pkt.WriteBEUInt8(Pig.IsSaddled() ? 1 : 0);
break;
} // case mtPig
case mtSheep:
2014-09-08 17:24:33 +00:00
{
auto & Sheep = static_cast<const cSheep &>(a_Mob);
a_Pkt.WriteBEUInt8(0x0c);
2015-11-29 18:13:31 +00:00
a_Pkt.WriteBEInt8(Sheep.IsBaby() ? -1 : (Sheep.IsInLoveCooldown() ? 1 : 0));
2016-02-05 21:45:45 +00:00
a_Pkt.WriteBEUInt8(0x10);
2014-09-08 17:24:33 +00:00
Byte SheepMetadata = 0;
SheepMetadata = static_cast<Byte>(Sheep.GetFurColor());
if (Sheep.IsSheared())
2014-09-08 17:24:33 +00:00
{
SheepMetadata |= 0x10;
}
a_Pkt.WriteBEUInt8(SheepMetadata);
2014-09-08 17:24:33 +00:00
break;
} // case mtSheep
case mtRabbit:
{
auto & Rabbit = static_cast<const cRabbit &>(a_Mob);
a_Pkt.WriteBEUInt8(0x12);
2016-08-24 19:45:03 +00:00
a_Pkt.WriteBEUInt8(static_cast<UInt8>(Rabbit.GetRabbitType()));
a_Pkt.WriteBEUInt8(0x0c);
2015-11-29 18:13:31 +00:00
a_Pkt.WriteBEInt8(Rabbit.IsBaby() ? -1 : (Rabbit.IsInLoveCooldown() ? 1 : 0));
break;
} // case mtRabbit
case mtSlime:
2014-09-08 17:24:33 +00:00
{
auto & Slime = static_cast<const cSlime &>(a_Mob);
a_Pkt.WriteBEUInt8(0x10);
a_Pkt.WriteBEUInt8(static_cast<UInt8>(Slime.GetSize()));
2014-09-08 17:24:33 +00:00
break;
} // case mtSlime
2014-09-08 15:02:54 +00:00
case mtVillager:
2014-09-08 17:24:33 +00:00
{
auto & Villager = static_cast<const cVillager &>(a_Mob);
a_Pkt.WriteBEUInt8(0x50);
a_Pkt.WriteBEInt32(Villager.GetVilType());
a_Pkt.WriteBEUInt8(0x0c);
2015-08-24 21:05:15 +00:00
a_Pkt.WriteBEInt8(Villager.IsBaby() ? -1 : 0);
2014-09-08 17:24:33 +00:00
break;
} // case mtVillager
case mtWitch:
2014-09-08 17:24:33 +00:00
{
auto & Witch = static_cast<const cWitch &>(a_Mob);
a_Pkt.WriteBEUInt8(0x15);
a_Pkt.WriteBEUInt8(Witch.IsAngry() ? 1 : 0);
2014-09-08 17:24:33 +00:00
break;
} // case mtWitch
case mtWither:
2014-09-08 17:24:33 +00:00
{
auto & Wither = static_cast<const cWither &>(a_Mob);
a_Pkt.WriteBEUInt8(0x54); // Int at index 20
a_Pkt.WriteBEInt32(static_cast<Int32>(Wither.GetWitherInvulnerableTicks()));
a_Pkt.WriteBEUInt8(0x66); // Float at index 6
a_Pkt.WriteBEFloat(static_cast<float>(a_Mob.GetHealth()));
2014-09-08 17:24:33 +00:00
break;
} // case mtWither
2020-04-04 11:44:17 +00:00
case mtWitherSkeleton:
{
a_Pkt.WriteBEUInt8(0x0d);
a_Pkt.WriteBEUInt8(1); // Is wither skeleton
break;
} // case mtWitherSkeleton
case mtWolf:
2014-09-08 17:24:33 +00:00
{
auto & Wolf = static_cast<const cWolf &>(a_Mob);
Byte WolfStatus = 0;
if (Wolf.IsSitting())
2014-09-08 17:24:33 +00:00
{
WolfStatus |= 0x1;
2014-09-08 17:24:33 +00:00
}
if (Wolf.IsAngry())
2014-09-08 17:24:33 +00:00
{
WolfStatus |= 0x2;
2014-09-08 17:24:33 +00:00
}
if (Wolf.IsTame())
2014-09-08 17:24:33 +00:00
{
WolfStatus |= 0x4;
2014-09-08 17:24:33 +00:00
}
a_Pkt.WriteBEUInt8(0x10);
a_Pkt.WriteBEUInt8(WolfStatus);
a_Pkt.WriteBEUInt8(0x72);
a_Pkt.WriteBEFloat(static_cast<float>(a_Mob.GetHealth()));
a_Pkt.WriteBEUInt8(0x13);
a_Pkt.WriteBEUInt8(Wolf.IsBegging() ? 1 : 0);
a_Pkt.WriteBEUInt8(0x14);
a_Pkt.WriteBEUInt8(static_cast<UInt8>(Wolf.GetCollarColor()));
a_Pkt.WriteBEUInt8(0x0c);
2015-08-24 21:05:15 +00:00
a_Pkt.WriteBEInt8(Wolf.IsBaby() ? -1 : 0);
2014-09-08 17:24:33 +00:00
break;
} // case mtWolf
case mtZombie:
{
auto & Zombie = static_cast<const cZombie &>(a_Mob);
a_Pkt.WriteBEUInt8(0x0c);
a_Pkt.WriteBEInt8(Zombie.IsBaby() ? 1 : -1);
a_Pkt.WriteBEUInt8(0x0d);
2018-02-03 11:24:19 +00:00
a_Pkt.WriteBEUInt8(0);
a_Pkt.WriteBEUInt8(0x0e);
2018-02-03 11:24:19 +00:00
a_Pkt.WriteBEUInt8(0);
break;
} // case mtZombie
case mtZombiePigman:
{
auto & ZombiePigman = static_cast<const cZombiePigman &>(a_Mob);
a_Pkt.WriteBEUInt8(0x0c);
a_Pkt.WriteBEInt8(ZombiePigman.IsBaby() ? 1 : -1);
break;
} // case mtZombiePigman
2018-02-03 11:24:19 +00:00
case mtZombieVillager:
{
auto & ZombieVillager = reinterpret_cast<const cZombieVillager &>(a_Mob);
a_Pkt.WriteBEUInt8(0x0c);
a_Pkt.WriteBEInt8(ZombieVillager.IsBaby() ? 1 : -1);
a_Pkt.WriteBEUInt8(0x0d);
a_Pkt.WriteBEUInt8(1);
a_Pkt.WriteBEUInt8(0x0e);
a_Pkt.WriteBEUInt8((ZombieVillager.ConversionTime() == -1) ? 0 : 1);
break;
} // case mtZombieVillager
default: break;
2014-09-08 17:24:33 +00:00
} // switch (a_Mob.GetType())
2014-09-08 15:02:54 +00:00
}
2014-09-08 15:08:28 +00:00
2016-12-15 19:21:43 +00:00
void cProtocol_1_8_0::WriteEntityProperties(cPacketizer & a_Pkt, const cEntity & a_Entity)
2014-09-08 15:08:28 +00:00
{
2014-09-08 17:24:33 +00:00
if (!a_Entity.IsMob())
2014-09-08 15:08:28 +00:00
{
2014-09-08 17:24:33 +00:00
// No properties for anything else than mobs
a_Pkt.WriteBEInt32(0);
2014-09-08 17:24:33 +00:00
return;
2014-09-08 15:08:28 +00:00
}
2014-09-08 17:24:33 +00:00
// const cMonster & Mob = (const cMonster &)a_Entity;
2014-09-08 15:08:28 +00:00
2014-09-08 17:24:33 +00:00
// TODO: Send properties and modifiers based on the mob type
2016-02-05 21:45:45 +00:00
a_Pkt.WriteBEInt32(0); // NumProperties
2014-09-08 17:24:33 +00:00
}
2020-04-20 19:46:04 +00:00
void cProtocol_1_8_0::SendEntityTeleport(const cEntity & a_Entity)
2020-04-20 19:46:04 +00:00
{
cPacketizer Pkt(*this, pktTeleportEntity);
Pkt.WriteVarInt32(a_Entity.GetUniqueID());
Pkt.WriteFPInt(a_Entity.GetPosX());
Pkt.WriteFPInt(a_Entity.GetPosY());
Pkt.WriteFPInt(a_Entity.GetPosZ());
Pkt.WriteByteAngle(a_Entity.GetYaw());
Pkt.WriteByteAngle(a_Entity.GetPitch());
Pkt.WriteBool(a_Entity.IsOnGround());
2020-04-20 19:46:04 +00:00
}
UInt8 cProtocol_1_8_0::GetProtocolEntityType(const cEntity & a_Entity)
{
using Type = cEntity::eEntityType;
switch (a_Entity.GetEntityType())
{
case Type::etEnderCrystal: return 51;
case Type::etPickup: return 2;
case Type::etFallingBlock: return 70;
case Type::etMinecart: return 10;
case Type::etBoat: return 1;
case Type::etTNT: return 50;
case Type::etProjectile:
{
using PType = cProjectileEntity::eKind;
const auto & Projectile = static_cast<const cProjectileEntity &>(a_Entity);
switch (Projectile.GetProjectileKind())
{
case PType::pkArrow: return 60;
case PType::pkSnowball: return 61;
case PType::pkEgg: return 62;
case PType::pkGhastFireball: return 63;
case PType::pkFireCharge: return 64;
case PType::pkEnderPearl: return 65;
case PType::pkExpBottle: return 75;
case PType::pkSplashPotion: return 73;
case PType::pkFirework: return 76;
case PType::pkWitherSkull: return 66;
}
}
case Type::etFloater: return 90;
case Type::etItemFrame: return 71;
case Type::etLeashKnot: return 77;
// Non-objects must not be sent
case Type::etEntity:
case Type::etPlayer:
case Type::etMonster:
case Type::etExpOrb:
case Type::etPainting: UNREACHABLE("Tried to spawn an unhandled entity");
}
2020-05-06 07:24:39 +00:00
UNREACHABLE("Unhandled entity kind");
2020-04-20 19:46:04 +00:00
}