eb0f640fa0
* Initial work on 1.13 entity metadata * Largely complete metadata Co-authored-by: Tiger Wang <ziwei.tiger@outlook.com>
1219 lines
40 KiB
C++
1219 lines
40 KiB
C++
|
|
// Protocol_1_13.cpp
|
|
|
|
/*
|
|
Implements the 1.13 protocol classes:
|
|
- release 1.13 protocol (#393)
|
|
*/
|
|
|
|
#include "Globals.h"
|
|
#include "Protocol_1_13.h"
|
|
#include "ProtocolRecognizer.h"
|
|
#include "ChunkDataSerializer.h"
|
|
#include "Packetizer.h"
|
|
#include "ProtocolPalettes.h"
|
|
|
|
#include "../Entities/Boat.h"
|
|
#include "../Entities/Minecart.h"
|
|
#include "../Entities/Pickup.h"
|
|
#include "../Entities/Player.h"
|
|
#include "../Entities/ItemFrame.h"
|
|
#include "../Entities/ArrowEntity.h"
|
|
#include "../Entities/FireworkEntity.h"
|
|
#include "../Entities/SplashPotionEntity.h"
|
|
|
|
#include "../Mobs/IncludeAllMonsters.h"
|
|
|
|
#include "../BlockTypePalette.h"
|
|
#include "../ClientHandle.h"
|
|
#include "../Root.h"
|
|
#include "../Server.h"
|
|
#include "../World.h"
|
|
#include "../JsonUtils.h"
|
|
|
|
#include "../Bindings/PluginManager.h"
|
|
|
|
|
|
|
|
|
|
|
|
#define HANDLE_READ(ByteBuf, Proc, Type, Var) \
|
|
Type Var; \
|
|
do { \
|
|
if (!ByteBuf.Proc(Var))\
|
|
{\
|
|
return;\
|
|
} \
|
|
} while (false)
|
|
|
|
|
|
|
|
|
|
|
|
#define HANDLE_PACKET_READ(ByteBuf, Proc, Type, Var) \
|
|
Type Var; \
|
|
do { \
|
|
{ \
|
|
if (!ByteBuf.Proc(Var)) \
|
|
{ \
|
|
ByteBuf.CheckValid(); \
|
|
return false; \
|
|
} \
|
|
ByteBuf.CheckValid(); \
|
|
} \
|
|
} while (false)
|
|
|
|
|
|
|
|
|
|
|
|
cProtocol_1_13::cProtocol_1_13(cClientHandle * a_Client, const AString & a_ServerAddress, UInt16 a_ServerPort, UInt32 a_State) :
|
|
Super(a_Client, a_ServerAddress, a_ServerPort, a_State)
|
|
{
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_13::Initialize(cClientHandle & a_Client)
|
|
{
|
|
// Get the palettes; fail if not available:
|
|
auto paletteVersion = this->GetPaletteVersion();
|
|
m_BlockTypePalette = cRoot::Get()->GetProtocolPalettes().blockTypePalette(paletteVersion);
|
|
if (m_BlockTypePalette == nullptr)
|
|
{
|
|
throw std::runtime_error(Printf("This server doesn't support protocol %s.", paletteVersion));
|
|
}
|
|
|
|
// Process the palette into the temporary BLOCKTYPE -> NetBlockID map:
|
|
auto upg = cRoot::Get()->GetUpgradeBlockTypePalette();
|
|
m_BlockTypeMap = m_BlockTypePalette->createTransformMapWithFallback(upg, 0);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
AString cProtocol_1_13::GetPaletteVersion() const
|
|
{
|
|
return "1.13";
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_13::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
|
|
{
|
|
ASSERT(m_State == 3); // In game mode?
|
|
|
|
cPacketizer Pkt(*this, pktBlockChange);
|
|
Pkt.WritePosition64(a_BlockX, a_BlockY, a_BlockZ);
|
|
Pkt.WriteVarInt32(static_cast<UInt32>(a_BlockType)); // TODO: Palette
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_13::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes)
|
|
{
|
|
ASSERT(m_State == 3); // In game mode?
|
|
|
|
cPacketizer Pkt(*this, pktBlockChanges);
|
|
Pkt.WriteBEInt32(a_ChunkX);
|
|
Pkt.WriteBEInt32(a_ChunkZ);
|
|
Pkt.WriteVarInt32(static_cast<UInt32>(a_Changes.size()));
|
|
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)); // TODO: Palette
|
|
} // for itr - a_Changes[]
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_13::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer)
|
|
{
|
|
ASSERT(m_State == 3); // In game mode?
|
|
|
|
const AString & ChunkData = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_13, a_ChunkX, a_ChunkZ, m_BlockTypeMap);
|
|
cCSLock Lock(m_CSPacket);
|
|
SendData(ChunkData.data(), ChunkData.size());
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_13::SendMapData(const cMap & a_Map, int a_DataStartX, int a_DataStartY)
|
|
{
|
|
// TODO
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_13::SendPaintingSpawn(const cPainting & a_Painting)
|
|
{
|
|
// TODO
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_13::SendParticleEffect(const AString & a_ParticleName, Vector3f a_Src, Vector3f a_Offset, float a_ParticleData, int a_ParticleAmount, std::array<int, 2> a_Data)
|
|
{
|
|
// This packet is unchanged since 1.8
|
|
// However we are hardcoding a string-to-id mapping inside there
|
|
// TODO: make a virtual enum-to-id mapping
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_13::SendPluginMessage(const AString & a_Channel, const AString & a_Message)
|
|
{
|
|
// TODO
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_13::SendScoreboardObjective(const AString & a_Name, const AString & a_DisplayName, Byte a_Mode)
|
|
{
|
|
// TODO
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_13::SendStatistics(const cStatManager & a_Manager)
|
|
{
|
|
// TODO
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_13::SendTabCompletionResults(const AStringVector & a_Results)
|
|
{
|
|
// TODO
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_13::SendUpdateBlockEntity(cBlockEntity & a_BlockEntity)
|
|
{
|
|
// TODO
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cProtocol_1_13::HandlePacket(cByteBuffer & a_ByteBuffer, UInt32 a_PacketType)
|
|
{
|
|
if (m_State != 3)
|
|
{
|
|
return Super::HandlePacket(a_ByteBuffer, a_PacketType);
|
|
}
|
|
|
|
// Game
|
|
switch (a_PacketType)
|
|
{
|
|
case 0x00: HandleConfirmTeleport(a_ByteBuffer); return true;
|
|
case 0x05: HandlePacketTabComplete(a_ByteBuffer); return true;
|
|
case 0x02: HandlePacketChatMessage(a_ByteBuffer); return true;
|
|
case 0x03: HandlePacketClientStatus(a_ByteBuffer); return true;
|
|
case 0x04: HandlePacketClientSettings(a_ByteBuffer); return true;
|
|
case 0x06: break; // Confirm transaction - not used in Cuberite
|
|
case 0x07: HandlePacketEnchantItem(a_ByteBuffer); return true;
|
|
case 0x08: HandlePacketWindowClick(a_ByteBuffer); return true;
|
|
case 0x09: HandlePacketWindowClose(a_ByteBuffer); return true;
|
|
case 0x0a: HandlePacketPluginMessage(a_ByteBuffer); return true;
|
|
case 0x0d: HandlePacketUseEntity(a_ByteBuffer); return true;
|
|
case 0x0e: HandlePacketKeepAlive(a_ByteBuffer); return true;
|
|
case 0x0f: HandlePacketPlayer(a_ByteBuffer); return true;
|
|
case 0x10: HandlePacketPlayerPos(a_ByteBuffer); return true;
|
|
case 0x11: HandlePacketPlayerPosLook(a_ByteBuffer); return true;
|
|
case 0x12: HandlePacketPlayerLook(a_ByteBuffer); return true;
|
|
case 0x13: HandlePacketVehicleMove(a_ByteBuffer); return true;
|
|
case 0x14: HandlePacketBoatSteer(a_ByteBuffer); return true;
|
|
case 0x15: break; // Pick item - not yet implemented
|
|
case 0x16: break; // Craft Recipe Request - not yet implemented
|
|
case 0x17: HandlePacketPlayerAbilities(a_ByteBuffer); return true;
|
|
case 0x18: HandlePacketBlockDig(a_ByteBuffer); return true;
|
|
case 0x19: HandlePacketEntityAction(a_ByteBuffer); return true;
|
|
case 0x1a: HandlePacketSteerVehicle(a_ByteBuffer); return true;
|
|
case 0x1b: HandlePacketCraftingBookData(a_ByteBuffer); return true;
|
|
case 0x1d: break; // Resource pack status - not yet implemented
|
|
case 0x1e: HandlePacketAdvancementTab(a_ByteBuffer); return true;
|
|
case 0x21: HandlePacketSlotSelect(a_ByteBuffer); return true;
|
|
case 0x24: HandlePacketCreativeInventoryAction(a_ByteBuffer); return true;
|
|
case 0x26: HandlePacketUpdateSign(a_ByteBuffer); return true;
|
|
case 0x27: HandlePacketAnimation(a_ByteBuffer); return true;
|
|
case 0x28: HandlePacketSpectate(a_ByteBuffer); return true;
|
|
case 0x29: HandlePacketBlockPlace(a_ByteBuffer); return true;
|
|
case 0x2a: HandlePacketUseItem(a_ByteBuffer); return true;
|
|
}
|
|
|
|
return Super::HandlePacket(a_ByteBuffer, a_PacketType);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_13::HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer)
|
|
{
|
|
cServer * Server = cRoot::Get()->GetServer();
|
|
AString ServerDescription = Server->GetDescription();
|
|
auto NumPlayers = static_cast<signed>(Server->GetNumPlayers());
|
|
auto MaxPlayers = static_cast<signed>(Server->GetMaxPlayers());
|
|
AString Favicon = Server->GetFaviconData();
|
|
cRoot::Get()->GetPluginManager()->CallHookServerPing(*m_Client, ServerDescription, NumPlayers, MaxPlayers, Favicon);
|
|
|
|
// Version:
|
|
Json::Value Version;
|
|
Version["name"] = "Cuberite 1.13";
|
|
Version["protocol"] = cMultiVersionProtocol::PROTO_VERSION_1_13;
|
|
|
|
// 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);
|
|
if (!Favicon.empty())
|
|
{
|
|
ResponseValue["favicon"] = Printf("data:image/png;base64,%s", Favicon.c_str());
|
|
}
|
|
|
|
// Serialize the response into a packet:
|
|
cPacketizer Pkt(*this, pktStatusResponse);
|
|
Pkt.WriteString(JsonUtils::WriteFastString(ResponseValue));
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_13::HandlePacketPluginMessage(cByteBuffer & a_ByteBuffer)
|
|
{
|
|
HANDLE_READ(a_ByteBuffer, ReadVarUTF8String, AString, Channel);
|
|
|
|
// If the plugin channel is recognized vanilla, handle it directly:
|
|
if (Channel.substr(0, 15) == "minecraft:brand")
|
|
{
|
|
HANDLE_READ(a_ByteBuffer, ReadVarUTF8String, AString, Brand);
|
|
m_Client->SetClientBrand(Brand);
|
|
|
|
// Send back our brand, including the length:
|
|
SendPluginMessage("minecraft:brand", "\x08""Cuberite");
|
|
return;
|
|
}
|
|
|
|
// Read the plugin message and relay to clienthandle:
|
|
AString Data;
|
|
VERIFY(a_ByteBuffer.ReadString(Data, a_ByteBuffer.GetReadableSpace() - 1)); // Always succeeds
|
|
m_Client->HandlePluginMessage(Channel, Data);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
UInt32 cProtocol_1_13::GetPacketID(ePacketType a_PacketType)
|
|
{
|
|
switch (a_PacketType)
|
|
{
|
|
case pktAttachEntity: return 0x46;
|
|
case pktBlockChanges: return 0x0f;
|
|
case pktCameraSetTo: return 0x3c;
|
|
case pktChatRaw: return 0x0e;
|
|
case pktCollectEntity: return 0x4f;
|
|
case pktDestroyEntity: return 0x35;
|
|
case pktDisconnectDuringGame: return 0x1b;
|
|
case pktEditSign: return 0x2c;
|
|
case pktEntityEffect: return 0x53;
|
|
case pktEntityEquipment: return 0x42;
|
|
case pktEntityHeadLook: return 0x39;
|
|
case pktEntityLook: return 0x2a;
|
|
case pktEntityMeta: return 0x3f;
|
|
case pktEntityProperties: return 0x52;
|
|
case pktEntityRelMove: return 0x28;
|
|
case pktEntityRelMoveLook: return 0x29;
|
|
case pktEntityStatus: return 0x1c;
|
|
case pktEntityVelocity: return 0x41;
|
|
case pktExperience: return 0x43;
|
|
case pktExplosion: return 0x1e;
|
|
case pktGameMode: return 0x20;
|
|
case pktHeldItemChange: return 0x3d;
|
|
case pktInventorySlot: return 0x17;
|
|
case pktJoinGame: return 0x25;
|
|
case pktKeepAlive: return 0x21;
|
|
case pktLeashEntity: return 0x40;
|
|
case pktMapData: return 0x26;
|
|
case pktParticleEffect: return 0x24;
|
|
case pktPlayerAbilities: return 0x2e;
|
|
case pktPlayerList: return 0x30;
|
|
case pktPlayerMaxSpeed: return 0x52;
|
|
case pktPlayerMoveLook: return 0x32;
|
|
case pktPluginMessage: return 0x19;
|
|
case pktRemoveEntityEffect: return 0x36;
|
|
case pktRespawn: return 0x38;
|
|
case pktScoreboardObjective: return 0x45;
|
|
case pktSoundEffect: return 0x1a;
|
|
case pktSoundParticleEffect: return 0x23;
|
|
case pktSpawnPosition: return 0x49;
|
|
case pktTabCompletionResults: return 0x10;
|
|
case pktTeleportEntity: return 0x50;
|
|
case pktTimeUpdate: return 0x4a;
|
|
case pktTitle: return 0x4b;
|
|
case pktUnloadChunk: return 0x1f;
|
|
case pktUnlockRecipe: return 0x32;
|
|
case pktUpdateHealth: return 0x44;
|
|
case pktUpdateScore: return 0x48;
|
|
case pktUpdateSign: return GetPacketID(pktUpdateBlockEntity);
|
|
case pktUseBed: return 0x33;
|
|
case pktWindowClose: return 0x13;
|
|
case pktWindowItems: return 0x15;
|
|
case pktWindowOpen: return 0x14;
|
|
case pktWindowProperty: return 0x16;
|
|
default: return Super::GetPacketID(a_PacketType);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
UInt32 cProtocol_1_13::GetProtocolMobType(eMonsterType a_MobType)
|
|
{
|
|
switch (a_MobType)
|
|
{
|
|
// Map invalid type to Giant for easy debugging (if this ever spawns, something has gone very wrong)
|
|
case mtInvalidType: return 27;
|
|
case mtBat: return 3;
|
|
case mtBlaze: return 4;
|
|
case mtCaveSpider: return 6;
|
|
case mtChicken: return 7;
|
|
case mtCow: return 9;
|
|
case mtCreeper: return 10;
|
|
case mtEnderDragon: return 17;
|
|
case mtEnderman: return 18;
|
|
case mtGhast: return 26;
|
|
case mtGiant: return 27;
|
|
case mtGuardian: return 28;
|
|
case mtHorse: return 29;
|
|
case mtIronGolem: return 80;
|
|
case mtMagmaCube: return 38;
|
|
case mtMooshroom: return 47;
|
|
case mtOcelot: return 48;
|
|
case mtPig: return 51;
|
|
case mtRabbit: return 56;
|
|
case mtSheep: return 58;
|
|
case mtSilverfish: return 61;
|
|
case mtSkeleton: return 62;
|
|
case mtSlime: return 64;
|
|
case mtSnowGolem: return 66;
|
|
case mtSpider: return 69;
|
|
case mtSquid: return 70;
|
|
case mtVillager: return 79;
|
|
case mtWitch: return 82;
|
|
case mtWither: return 83;
|
|
case mtWitherSkeleton: return 84;
|
|
case mtWolf: return 86;
|
|
case mtZombie: return 87;
|
|
case mtZombiePigman: return 53;
|
|
case mtZombieVillager: return 89;
|
|
}
|
|
UNREACHABLE("Unsupported mob type");
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
UInt8 cProtocol_1_13::GetEntityMetadataID(eEntityMetadata a_Metadata)
|
|
{
|
|
const UInt8 Entity = 6;
|
|
const UInt8 Living = Entity + 5;
|
|
const UInt8 Insentient = Living + 1;
|
|
const UInt8 Ageable = Insentient + 1;
|
|
const UInt8 AbstractHorse = Ageable + 2;
|
|
const UInt8 ChestedHorse = AbstractHorse + 1;
|
|
const UInt8 TameableAnimal = Ageable + 2;
|
|
const UInt8 Minecart = Entity + 6;
|
|
|
|
switch (a_Metadata)
|
|
{
|
|
case eEntityMetadata::EntityFlags: return 0;
|
|
case eEntityMetadata::EntityAir: return 1;
|
|
case eEntityMetadata::EntityCustomName: return 2;
|
|
case eEntityMetadata::EntityCustomNameVisible: return 3;
|
|
case eEntityMetadata::EntitySilent: return 4;
|
|
case eEntityMetadata::EntityNoGravity: return 5;
|
|
case eEntityMetadata::PotionThrown: return Entity;
|
|
case eEntityMetadata::FallingBlockPosition: return Entity;
|
|
case eEntityMetadata::AreaEffectCloudRadius: return Entity;
|
|
case eEntityMetadata::AreaEffectCloudColor: return Entity + 1;
|
|
case eEntityMetadata::AreaEffectCloudSinglePointEffect: return Entity + 2;
|
|
case eEntityMetadata::AreaEffectCloudParticleId: return Entity + 3;
|
|
case eEntityMetadata::ArrowFlags: return Entity;
|
|
case eEntityMetadata::TippedArrowColor: return Entity + 1;
|
|
case eEntityMetadata::BoatLastHitTime: return Entity;
|
|
case eEntityMetadata::BoatForwardDirection: return Entity + 1;
|
|
case eEntityMetadata::BoatDamageTaken: return Entity + 2;
|
|
case eEntityMetadata::BoatType: return Entity + 3;
|
|
case eEntityMetadata::BoatLeftPaddleTurning: return Entity + 4;
|
|
case eEntityMetadata::BoatRightPaddleTurning: return Entity + 5;
|
|
case eEntityMetadata::BoatSplashTimer: return Entity + 6;
|
|
case eEntityMetadata::EnderCrystalBeamTarget: return Entity;
|
|
case eEntityMetadata::EnderCrystalShowBottom: return Entity + 1;
|
|
case eEntityMetadata::WitherSkullInvulnerable: return Entity;
|
|
case eEntityMetadata::FireworkInfo: return Entity;
|
|
case eEntityMetadata::FireworkBoostedEntityId: return Entity + 1;
|
|
case eEntityMetadata::ItemFrameItem: return Entity;
|
|
case eEntityMetadata::ItemFrameRotation: return Entity + 1;
|
|
case eEntityMetadata::ItemItem: return Entity;
|
|
case eEntityMetadata::LivingActiveHand: return Entity;
|
|
case eEntityMetadata::LivingHealth: return Entity + 1;
|
|
case eEntityMetadata::LivingPotionEffectColor: return Entity + 2;
|
|
case eEntityMetadata::LivingPotionEffectAmbient: return Entity + 3;
|
|
case eEntityMetadata::LivingNumberOfArrows: return Entity + 4;
|
|
case eEntityMetadata::PlayerAdditionalHearts: return Living;
|
|
case eEntityMetadata::PlayerScore: return Living + 1;
|
|
case eEntityMetadata::PlayerDisplayedSkinParts: return Living + 2;
|
|
case eEntityMetadata::PlayerMainHand: return Living + 3;
|
|
case eEntityMetadata::ArmorStandStatus: return Living;
|
|
case eEntityMetadata::ArmorStandHeadRotation: return Living + 1;
|
|
case eEntityMetadata::ArmorStandBodyRotation: return Living + 2;
|
|
case eEntityMetadata::ArmorStandLeftArmRotation: return Living + 3;
|
|
case eEntityMetadata::ArmorStandRightArmRotation: return Living + 4;
|
|
case eEntityMetadata::ArmorStandLeftLegRotation: return Living + 5;
|
|
case eEntityMetadata::ArmorStandRightLegRotation: return Living + 6;
|
|
case eEntityMetadata::InsentientFlags: return Living;
|
|
case eEntityMetadata::BatHanging: return Insentient;
|
|
case eEntityMetadata::AgeableIsBaby: return Insentient;
|
|
case eEntityMetadata::AbstractHorseFlags: return Ageable;
|
|
case eEntityMetadata::AbstractHorseOwner: return Ageable + 1;
|
|
case eEntityMetadata::HorseVariant: return AbstractHorse;
|
|
case eEntityMetadata::HorseArmour: return AbstractHorse + 1;
|
|
case eEntityMetadata::ChestedHorseChested: return AbstractHorse;
|
|
case eEntityMetadata::LlamaStrength: return ChestedHorse;
|
|
case eEntityMetadata::LlamaCarpetColor: return ChestedHorse + 1;
|
|
case eEntityMetadata::LlamaVariant: return ChestedHorse + 2;
|
|
case eEntityMetadata::PigHasSaddle: return Ageable;
|
|
case eEntityMetadata::PigTotalCarrotOnAStickBoost: return Ageable + 1;
|
|
case eEntityMetadata::RabbitType: return Ageable;
|
|
case eEntityMetadata::PolarBearStanding: return Ageable;
|
|
case eEntityMetadata::SheepFlags: return Ageable;
|
|
case eEntityMetadata::TameableAnimalFlags: return Ageable;
|
|
case eEntityMetadata::TameableAnimalOwner: return Ageable + 1;
|
|
case eEntityMetadata::OcelotType: return TameableAnimal;
|
|
case eEntityMetadata::WolfDamageTaken: return TameableAnimal;
|
|
case eEntityMetadata::WolfBegging: return TameableAnimal + 1;
|
|
case eEntityMetadata::WolfCollarColour: return TameableAnimal + 2;
|
|
case eEntityMetadata::VillagerProfession: return Ageable;
|
|
case eEntityMetadata::IronGolemPlayerCreated: return Insentient;
|
|
case eEntityMetadata::ShulkerFacingDirection: return Insentient;
|
|
case eEntityMetadata::ShulkerAttachmentFallingBlockPosition: return Insentient + 1;
|
|
case eEntityMetadata::ShulkerShieldHeight: return Insentient + 2;
|
|
case eEntityMetadata::BlazeOnFire: return Insentient;
|
|
case eEntityMetadata::CreeperState: return Insentient;
|
|
case eEntityMetadata::CreeperPowered: return Insentient + 1;
|
|
case eEntityMetadata::CreeperIgnited: return Insentient + 2;
|
|
case eEntityMetadata::GuardianStatus: return Insentient;
|
|
case eEntityMetadata::GuardianTarget: return Insentient + 1;
|
|
case eEntityMetadata::IllagerFlags: return Insentient;
|
|
case eEntityMetadata::SpeIlagerSpell: return Insentient + 1;
|
|
case eEntityMetadata::VexFlags: return Insentient;
|
|
case eEntityMetadata::SpiderClimbing: return Insentient;
|
|
case eEntityMetadata::WitchAggresive: return Insentient;
|
|
case eEntityMetadata::WitherFirstHeadTarget: return Insentient;
|
|
case eEntityMetadata::WitherSecondHeadTarget: return Insentient + 1;
|
|
case eEntityMetadata::WitherThirdHeadTarget: return Insentient + 2;
|
|
case eEntityMetadata::WitherInvulnerableTimer: return Insentient + 3;
|
|
case eEntityMetadata::ZombieIsBaby: return Insentient;
|
|
case eEntityMetadata::ZombieHandsRisedUp: return Insentient + 2;
|
|
case eEntityMetadata::ZombieVillagerConverting: return Insentient + 4;
|
|
case eEntityMetadata::ZombieVillagerProfession: return Insentient + 5;
|
|
case eEntityMetadata::EndermanCarriedBlock: return Insentient;
|
|
case eEntityMetadata::EndermanScreaming: return Insentient + 1;
|
|
case eEntityMetadata::EnderDragonDragonPhase: return Insentient;
|
|
case eEntityMetadata::GhastAttacking: return Insentient;
|
|
case eEntityMetadata::SlimeSize: return Insentient;
|
|
case eEntityMetadata::MinecartShakingPower: return Entity;
|
|
case eEntityMetadata::MinecartShakingDirection: return Entity + 1;
|
|
case eEntityMetadata::MinecartShakingMultiplier: return Entity + 2;
|
|
case eEntityMetadata::MinecartBlockIDMeta: return Entity + 3;
|
|
case eEntityMetadata::MinecartBlockY: return Entity + 4;
|
|
case eEntityMetadata::MinecartShowBlock: return Entity + 5;
|
|
case eEntityMetadata::MinecartCommandBlockCommand: return Minecart;
|
|
case eEntityMetadata::MinecartCommandBlockLastOutput: return Minecart + 1;
|
|
case eEntityMetadata::MinecartFurnacePowered: return Minecart;
|
|
case eEntityMetadata::TNTPrimedFuseTime: return Entity;
|
|
|
|
case eEntityMetadata::EntityPose:
|
|
case eEntityMetadata::AreaEffectCloudParticleParameter1:
|
|
case eEntityMetadata::AreaEffectCloudParticleParameter2:
|
|
case eEntityMetadata::AbstractSkeletonArmsSwinging:
|
|
case eEntityMetadata::ZombieUnusedWasType: UNREACHABLE("Retrieved invalid metadata for protocol");
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
UInt8 cProtocol_1_13::GetEntityMetadataID(eEntityMetadataType a_FieldType)
|
|
{
|
|
switch (a_FieldType)
|
|
{
|
|
case eEntityMetadataType::Byte: return 0;
|
|
case eEntityMetadataType::VarInt: return 1;
|
|
case eEntityMetadataType::Float: return 2;
|
|
case eEntityMetadataType::String: return 3;
|
|
case eEntityMetadataType::Chat: return 4;
|
|
case eEntityMetadataType::OptChat: return 5;
|
|
case eEntityMetadataType::Item: return 6;
|
|
case eEntityMetadataType::Boolean: return 7;
|
|
case eEntityMetadataType::Rotation: return 8;
|
|
case eEntityMetadataType::Position: return 9;
|
|
case eEntityMetadataType::OptPosition: return 10;
|
|
case eEntityMetadataType::Direction: return 11;
|
|
case eEntityMetadataType::OptUUID: return 12;
|
|
case eEntityMetadataType::OptBlockID: return 13;
|
|
case eEntityMetadataType::NBT: return 14;
|
|
case eEntityMetadataType::Particle: return 15;
|
|
case eEntityMetadataType::VillagerData: return 16;
|
|
case eEntityMetadataType::OptVarInt: return 17;
|
|
case eEntityMetadataType::Pose: return 18;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cProtocol_1_13::ReadItem(cByteBuffer & a_ByteBuffer, cItem & a_Item, size_t a_KeepRemainingBytes)
|
|
{
|
|
HANDLE_PACKET_READ(a_ByteBuffer, ReadBEInt16, Int16, ItemType);
|
|
if (ItemType == -1)
|
|
{
|
|
// The item is empty, no more data follows
|
|
a_Item.Empty();
|
|
return true;
|
|
}
|
|
a_Item.m_ItemType = ItemType;
|
|
|
|
HANDLE_PACKET_READ(a_ByteBuffer, ReadBEInt8, Int8, ItemCount);
|
|
a_Item.m_ItemCount = ItemCount;
|
|
a_Item.m_ItemDamage = 0; // o no, no more damage in 1.13
|
|
if (ItemCount <= 0)
|
|
{
|
|
a_Item.Empty();
|
|
}
|
|
|
|
AString Metadata;
|
|
if (!a_ByteBuffer.ReadString(Metadata, a_ByteBuffer.GetReadableSpace() - a_KeepRemainingBytes - 1) || (Metadata.size() == 0) || (Metadata[0] == 0))
|
|
{
|
|
// No metadata
|
|
return true;
|
|
}
|
|
|
|
ParseItemMetadata(a_Item, Metadata);
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_13::WriteItem(cPacketizer & a_Pkt, const cItem & a_Item)
|
|
{
|
|
short ItemType = a_Item.m_ItemType;
|
|
ASSERT(ItemType >= -1); // Check validity of packets in debug runtime
|
|
if (ItemType <= 0)
|
|
{
|
|
// Fix, to make sure no invalid values are sent.
|
|
ItemType = -1;
|
|
}
|
|
|
|
if (a_Item.IsEmpty())
|
|
{
|
|
a_Pkt.WriteBEInt16(-1);
|
|
return;
|
|
}
|
|
|
|
// Normal item
|
|
// TODO: use new item ids
|
|
a_Pkt.WriteBEInt16(ItemType);
|
|
a_Pkt.WriteBEInt8(a_Item.m_ItemCount);
|
|
|
|
// TODO: NBT
|
|
a_Pkt.WriteBEInt8(0);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_13::WriteEntityMetadata(cPacketizer & a_Pkt, const eEntityMetadata a_Metadata, const eEntityMetadataType a_FieldType)
|
|
{
|
|
a_Pkt.WriteBEUInt8(GetEntityMetadataID(a_Metadata)); // Index
|
|
a_Pkt.WriteBEUInt8(GetEntityMetadataID(a_FieldType)); // Type
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_13::WriteEntityMetadata(cPacketizer & a_Pkt, const cEntity & a_Entity)
|
|
{
|
|
// Common metadata:
|
|
Int8 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;
|
|
}
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::EntityFlags, eEntityMetadataType::Byte);
|
|
a_Pkt.WriteBEInt8(Flags);
|
|
|
|
switch (a_Entity.GetEntityType())
|
|
{
|
|
case cEntity::etPlayer:
|
|
{
|
|
auto & Player = static_cast<const cPlayer &>(a_Entity);
|
|
|
|
// TODO Set player custom name to their name.
|
|
// Then it's possible to move the custom name of mobs to the entities
|
|
// and to remove the "special" player custom name.
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::EntityCustomName, eEntityMetadataType::String);
|
|
a_Pkt.WriteString(Player.GetName());
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::LivingHealth, eEntityMetadataType::Float);
|
|
a_Pkt.WriteBEFloat(static_cast<float>(Player.GetHealth()));
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::PlayerDisplayedSkinParts, eEntityMetadataType::Byte);
|
|
a_Pkt.WriteBEUInt8(static_cast<UInt8>(Player.GetSkinParts()));
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::PlayerMainHand, eEntityMetadataType::Byte);
|
|
a_Pkt.WriteBEUInt8(static_cast<UInt8>(Player.GetMainHand()));
|
|
break;
|
|
}
|
|
case cEntity::etPickup:
|
|
{
|
|
/* TODO
|
|
a_Pkt.WriteBEUInt8(ITEM_ITEM);
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_ITEM);
|
|
WriteItem(a_Pkt, static_cast<const cPickup &>(a_Entity).GetItem());
|
|
*/
|
|
break;
|
|
}
|
|
case cEntity::etMinecart:
|
|
{
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::MinecartShakingPower, eEntityMetadataType::VarInt);
|
|
|
|
// The following expression makes Minecarts shake more with less health or higher damage taken
|
|
auto & Minecart = static_cast<const cMinecart &>(a_Entity);
|
|
auto maxHealth = a_Entity.GetMaxHealth();
|
|
auto curHealth = a_Entity.GetHealth();
|
|
a_Pkt.WriteVarInt32(static_cast<UInt32>((maxHealth - curHealth) * Minecart.LastDamage() * 4));
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::MinecartShakingDirection, eEntityMetadataType::VarInt);
|
|
a_Pkt.WriteVarInt32(1); // (doesn't seem to effect anything)
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::MinecartShakingMultiplier, eEntityMetadataType::Float);
|
|
a_Pkt.WriteBEFloat(static_cast<float>(Minecart.LastDamage() + 10)); // or damage taken
|
|
|
|
if (Minecart.GetPayload() == cMinecart::mpNone)
|
|
{
|
|
auto & RideableMinecart = static_cast<const cRideableMinecart &>(Minecart);
|
|
const cItem & MinecartContent = RideableMinecart.GetContent();
|
|
if (!MinecartContent.IsEmpty())
|
|
{
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::MinecartBlockIDMeta, eEntityMetadataType::VarInt);
|
|
int Content = MinecartContent.m_ItemType;
|
|
Content |= MinecartContent.m_ItemDamage << 8;
|
|
a_Pkt.WriteVarInt32(static_cast<UInt32>(Content));
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::MinecartBlockY, eEntityMetadataType::VarInt);
|
|
a_Pkt.WriteVarInt32(static_cast<UInt32>(RideableMinecart.GetBlockHeight()));
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::MinecartShowBlock, eEntityMetadataType::Boolean);
|
|
a_Pkt.WriteBool(true);
|
|
}
|
|
}
|
|
else if (Minecart.GetPayload() == cMinecart::mpFurnace)
|
|
{
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::MinecartFurnacePowered, eEntityMetadataType::Boolean);
|
|
a_Pkt.WriteBool(static_cast<const cMinecartWithFurnace &>(Minecart).IsFueled());
|
|
}
|
|
break;
|
|
} // case etMinecart
|
|
|
|
case cEntity::etProjectile:
|
|
{
|
|
auto & Projectile = static_cast<const cProjectileEntity &>(a_Entity);
|
|
switch (Projectile.GetProjectileKind())
|
|
{
|
|
case cProjectileEntity::pkArrow:
|
|
{
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::ArrowFlags, eEntityMetadataType::Byte);
|
|
a_Pkt.WriteBEInt8(static_cast<const cArrowEntity &>(Projectile).IsCritical() ? 1 : 0);
|
|
break;
|
|
}
|
|
case cProjectileEntity::pkFirework:
|
|
{
|
|
// TODO
|
|
break;
|
|
}
|
|
case cProjectileEntity::pkSplashPotion:
|
|
{
|
|
// TODO
|
|
}
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
} // case etProjectile
|
|
|
|
case cEntity::etMonster:
|
|
{
|
|
WriteMobMetadata(a_Pkt, static_cast<const cMonster &>(a_Entity));
|
|
break;
|
|
}
|
|
|
|
case cEntity::etBoat:
|
|
{
|
|
auto & Boat = static_cast<const cBoat &>(a_Entity);
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::BoatLastHitTime, eEntityMetadataType::VarInt);
|
|
a_Pkt.WriteVarInt32(static_cast<UInt32>(Boat.GetLastDamage()));
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::BoatForwardDirection, eEntityMetadataType::VarInt);
|
|
a_Pkt.WriteVarInt32(static_cast<UInt32>(Boat.GetForwardDirection()));
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::BoatDamageTaken, eEntityMetadataType::Float);
|
|
a_Pkt.WriteBEFloat(Boat.GetDamageTaken());
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::BoatType, eEntityMetadataType::VarInt);
|
|
a_Pkt.WriteVarInt32(static_cast<UInt32>(Boat.GetMaterial()));
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::BoatRightPaddleTurning, eEntityMetadataType::Boolean);
|
|
a_Pkt.WriteBool(Boat.IsRightPaddleUsed());
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::BoatLeftPaddleTurning, eEntityMetadataType::Boolean);
|
|
a_Pkt.WriteBool(static_cast<bool>(Boat.IsLeftPaddleUsed()));
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::BoatSplashTimer, eEntityMetadataType::VarInt);
|
|
a_Pkt.WriteVarInt32(0);
|
|
|
|
break;
|
|
} // case etBoat
|
|
|
|
case cEntity::etItemFrame:
|
|
{
|
|
// TODO
|
|
break;
|
|
} // case etItemFrame
|
|
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_13::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_Mob)
|
|
{
|
|
// Living Enitiy Metadata
|
|
if (a_Mob.HasCustomName())
|
|
{
|
|
// TODO: As of 1.9 _all_ entities can have custom names; should this be moved up?
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::EntityCustomName, eEntityMetadataType::OptChat);
|
|
a_Pkt.WriteBool(true);
|
|
a_Pkt.WriteString(a_Mob.GetCustomName());
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::EntityCustomNameVisible, eEntityMetadataType::Boolean);
|
|
a_Pkt.WriteBool(a_Mob.IsCustomNameAlwaysVisible());
|
|
}
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::LivingHealth, eEntityMetadataType::Float);
|
|
a_Pkt.WriteBEFloat(static_cast<float>(a_Mob.GetHealth()));
|
|
|
|
switch (a_Mob.GetMobType())
|
|
{
|
|
case mtBat:
|
|
{
|
|
auto & Bat = static_cast<const cBat &>(a_Mob);
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::BatHanging, eEntityMetadataType::Byte);
|
|
a_Pkt.WriteBEInt8(Bat.IsHanging() ? 1 : 0);
|
|
break;
|
|
} // case mtBat
|
|
|
|
case mtChicken:
|
|
{
|
|
auto & Chicken = static_cast<const cChicken &>(a_Mob);
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::AgeableIsBaby, eEntityMetadataType::Boolean);
|
|
a_Pkt.WriteBool(Chicken.IsBaby());
|
|
break;
|
|
} // case mtChicken
|
|
|
|
case mtCow:
|
|
{
|
|
auto & Cow = static_cast<const cCow &>(a_Mob);
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::AgeableIsBaby, eEntityMetadataType::Boolean);
|
|
a_Pkt.WriteBool(Cow.IsBaby());
|
|
break;
|
|
} // case mtCow
|
|
|
|
case mtCreeper:
|
|
{
|
|
auto & Creeper = static_cast<const cCreeper &>(a_Mob);
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::CreeperState, eEntityMetadataType::VarInt);
|
|
a_Pkt.WriteVarInt32(Creeper.IsBlowing() ? 1 : static_cast<UInt32>(-1)); // (idle or "blowing")
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::CreeperPowered, eEntityMetadataType::Boolean);
|
|
a_Pkt.WriteBool(Creeper.IsCharged());
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::CreeperIgnited, eEntityMetadataType::Boolean);
|
|
a_Pkt.WriteBool(Creeper.IsBurnedWithFlintAndSteel());
|
|
break;
|
|
} // case mtCreeper
|
|
|
|
case mtEnderman:
|
|
{
|
|
auto & Enderman = static_cast<const cEnderman &>(a_Mob);
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::EndermanCarriedBlock, eEntityMetadataType::OptBlockID);
|
|
UInt32 Carried = 0;
|
|
Carried |= static_cast<UInt32>(Enderman.GetCarriedBlock() << 4);
|
|
Carried |= Enderman.GetCarriedMeta();
|
|
a_Pkt.WriteVarInt32(Carried);
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::EndermanScreaming, eEntityMetadataType::Boolean);
|
|
a_Pkt.WriteBool(Enderman.IsScreaming());
|
|
break;
|
|
} // case mtEnderman
|
|
|
|
case mtGhast:
|
|
{
|
|
auto & Ghast = static_cast<const cGhast &>(a_Mob);
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::GhastAttacking, eEntityMetadataType::Boolean);
|
|
a_Pkt.WriteBool(Ghast.IsCharging());
|
|
break;
|
|
} // case mtGhast
|
|
|
|
case mtHorse:
|
|
{
|
|
// XXX This behaves incorrectly with different varients; horses have different entity IDs now
|
|
|
|
// Abstract horse
|
|
auto & Horse = static_cast<const cHorse &>(a_Mob);
|
|
|
|
Int8 Flags = 0;
|
|
if (Horse.IsTame())
|
|
{
|
|
Flags |= 0x02;
|
|
}
|
|
if (Horse.IsSaddled())
|
|
{
|
|
Flags |= 0x04;
|
|
}
|
|
if (Horse.IsInLoveCooldown())
|
|
{
|
|
Flags |= 0x08;
|
|
}
|
|
if (Horse.IsEating())
|
|
{
|
|
Flags |= 0x10;
|
|
}
|
|
if (Horse.IsRearing())
|
|
{
|
|
Flags |= 0x20;
|
|
}
|
|
if (Horse.IsMthOpen())
|
|
{
|
|
Flags |= 0x40;
|
|
}
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::AbstractHorseFlags, eEntityMetadataType::Byte);
|
|
a_Pkt.WriteBEInt8(Flags);
|
|
|
|
// Regular horses
|
|
int Appearance = 0;
|
|
Appearance = Horse.GetHorseColor();
|
|
Appearance |= Horse.GetHorseStyle() << 8;
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::HorseVariant, eEntityMetadataType::VarInt);
|
|
a_Pkt.WriteVarInt32(static_cast<UInt32>(Appearance)); // Color / style
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::HorseArmour, eEntityMetadataType::VarInt);
|
|
a_Pkt.WriteVarInt32(static_cast<UInt32>(Horse.GetHorseArmour()));
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::AgeableIsBaby, eEntityMetadataType::Boolean);
|
|
a_Pkt.WriteBool(Horse.IsBaby());
|
|
break;
|
|
} // case mtHorse
|
|
|
|
case mtMagmaCube:
|
|
{
|
|
auto & MagmaCube = static_cast<const cMagmaCube &>(a_Mob);
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::SlimeSize, eEntityMetadataType::VarInt);
|
|
a_Pkt.WriteVarInt32(static_cast<UInt32>(MagmaCube.GetSize()));
|
|
break;
|
|
} // case mtMagmaCube
|
|
|
|
case mtOcelot:
|
|
{
|
|
auto & Ocelot = static_cast<const cOcelot &>(a_Mob);
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::AgeableIsBaby, eEntityMetadataType::Boolean);
|
|
a_Pkt.WriteBool(Ocelot.IsBaby());
|
|
|
|
Int8 OcelotStatus = 0;
|
|
if (Ocelot.IsSitting())
|
|
{
|
|
OcelotStatus |= 0x1;
|
|
}
|
|
if (Ocelot.IsTame())
|
|
{
|
|
OcelotStatus |= 0x4;
|
|
}
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::TameableAnimalFlags, eEntityMetadataType::Byte);
|
|
a_Pkt.WriteBEInt8(OcelotStatus);
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::OcelotType, eEntityMetadataType::Byte);
|
|
a_Pkt.WriteVarInt32(static_cast<UInt32>(Ocelot.GetOcelotType()));
|
|
|
|
break;
|
|
} // case mtOcelot
|
|
|
|
case mtPig:
|
|
{
|
|
auto & Pig = static_cast<const cPig &>(a_Mob);
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::AgeableIsBaby, eEntityMetadataType::Boolean);
|
|
a_Pkt.WriteBool(Pig.IsBaby());
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::PigHasSaddle, eEntityMetadataType::Boolean);
|
|
a_Pkt.WriteBool(Pig.IsSaddled());
|
|
|
|
// PIG_TOTAL_CARROT_ON_A_STICK_BOOST in 1.11.1 only
|
|
break;
|
|
} // case mtPig
|
|
|
|
case mtRabbit:
|
|
{
|
|
auto & Rabbit = static_cast<const cRabbit &>(a_Mob);
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::AgeableIsBaby, eEntityMetadataType::Boolean);
|
|
a_Pkt.WriteBool(Rabbit.IsBaby());
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::RabbitType, eEntityMetadataType::VarInt);
|
|
a_Pkt.WriteVarInt32(static_cast<UInt32>(Rabbit.GetRabbitType()));
|
|
break;
|
|
} // case mtRabbit
|
|
|
|
case mtSheep:
|
|
{
|
|
auto & Sheep = static_cast<const cSheep &>(a_Mob);
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::AgeableIsBaby, eEntityMetadataType::Boolean);
|
|
a_Pkt.WriteBool(Sheep.IsBaby());
|
|
|
|
Int8 SheepMetadata = 0;
|
|
SheepMetadata = static_cast<Int8>(Sheep.GetFurColor());
|
|
if (Sheep.IsSheared())
|
|
{
|
|
SheepMetadata |= 0x10;
|
|
}
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::SheepFlags, eEntityMetadataType::Byte);
|
|
a_Pkt.WriteBEInt8(SheepMetadata);
|
|
break;
|
|
} // case mtSheep
|
|
|
|
case mtSlime:
|
|
{
|
|
auto & Slime = static_cast<const cSlime &>(a_Mob);
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::SlimeSize, eEntityMetadataType::VarInt);
|
|
a_Pkt.WriteVarInt32(static_cast<UInt32>(Slime.GetSize()));
|
|
break;
|
|
} // case mtSlime
|
|
|
|
case mtVillager:
|
|
{
|
|
auto & Villager = static_cast<const cVillager &>(a_Mob);
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::AgeableIsBaby, eEntityMetadataType::Boolean);
|
|
a_Pkt.WriteBool(Villager.IsBaby());
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::VillagerProfession, eEntityMetadataType::VarInt);
|
|
a_Pkt.WriteVarInt32(static_cast<UInt32>(Villager.GetVilType()));
|
|
break;
|
|
} // case mtVillager
|
|
|
|
case mtWitch:
|
|
{
|
|
auto & Witch = static_cast<const cWitch &>(a_Mob);
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::WitchAggresive, eEntityMetadataType::Boolean);
|
|
a_Pkt.WriteBool(Witch.IsAngry());
|
|
break;
|
|
} // case mtWitch
|
|
|
|
case mtWither:
|
|
{
|
|
auto & Wither = static_cast<const cWither &>(a_Mob);
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::WitherInvulnerableTimer, eEntityMetadataType::VarInt);
|
|
a_Pkt.WriteVarInt32(Wither.GetWitherInvulnerableTicks());
|
|
|
|
// TODO: Use boss bar packet for health
|
|
break;
|
|
} // case mtWither
|
|
|
|
case mtWolf:
|
|
{
|
|
auto & Wolf = static_cast<const cWolf &>(a_Mob);
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::AgeableIsBaby, eEntityMetadataType::Boolean);
|
|
a_Pkt.WriteBool(Wolf.IsBaby());
|
|
|
|
Int8 WolfStatus = 0;
|
|
if (Wolf.IsSitting())
|
|
{
|
|
WolfStatus |= 0x1;
|
|
}
|
|
if (Wolf.IsAngry())
|
|
{
|
|
WolfStatus |= 0x2;
|
|
}
|
|
if (Wolf.IsTame())
|
|
{
|
|
WolfStatus |= 0x4;
|
|
}
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::TameableAnimalFlags, eEntityMetadataType::Byte);
|
|
a_Pkt.WriteBEInt8(WolfStatus);
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::WolfDamageTaken, eEntityMetadataType::Float);
|
|
a_Pkt.WriteBEFloat(static_cast<float>(a_Mob.GetHealth())); // TODO Not use the current health
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::WolfBegging, eEntityMetadataType::Boolean);
|
|
a_Pkt.WriteBool(Wolf.IsBegging());
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::WolfCollarColour, eEntityMetadataType::VarInt);
|
|
a_Pkt.WriteVarInt32(static_cast<UInt32>(Wolf.GetCollarColor()));
|
|
break;
|
|
} // case mtWolf
|
|
|
|
case mtZombie:
|
|
{
|
|
// XXX Zombies were also split into new sublcasses; this doesn't handle that.
|
|
|
|
auto & Zombie = static_cast<const cZombie &>(a_Mob);
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::ZombieIsBaby, eEntityMetadataType::Boolean);
|
|
a_Pkt.WriteBool(Zombie.IsBaby());
|
|
break;
|
|
} // case mtZombie
|
|
|
|
case mtZombiePigman:
|
|
{
|
|
auto & ZombiePigman = static_cast<const cZombiePigman &>(a_Mob);
|
|
|
|
WriteEntityMetadata(a_Pkt, eEntityMetadata::AgeableIsBaby, eEntityMetadataType::Boolean);
|
|
a_Pkt.WriteBool(ZombiePigman.IsBaby());
|
|
break;
|
|
} // case mtZombiePigman
|
|
|
|
case mtBlaze:
|
|
case mtEnderDragon:
|
|
case mtGuardian:
|
|
case mtIronGolem:
|
|
case mtSnowGolem:
|
|
case mtSpider:
|
|
case mtZombieVillager:
|
|
{
|
|
// TODO: Mobs with extra fields that aren't implemented
|
|
break;
|
|
}
|
|
|
|
case mtMooshroom:
|
|
case mtCaveSpider:
|
|
{
|
|
// Not mentioned on http://wiki.vg/Entities
|
|
break;
|
|
}
|
|
|
|
case mtGiant:
|
|
case mtSilverfish:
|
|
case mtSkeleton:
|
|
case mtSquid:
|
|
case mtWitherSkeleton:
|
|
{
|
|
// Mobs with no extra fields
|
|
break;
|
|
}
|
|
|
|
case mtInvalidType:
|
|
{
|
|
ASSERT(!"cProtocol_1_13::WriteMobMetadata: Recieved mob of invalid type");
|
|
break;
|
|
}
|
|
} // switch (a_Mob.GetType())
|
|
}
|