summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/Entities/Boat.cpp15
-rw-r--r--src/Protocol/Protocol.h187
-rw-r--r--src/Protocol/Protocol_1_13.cpp1025
-rw-r--r--src/Protocol/Protocol_1_13.h40
4 files changed, 1086 insertions, 181 deletions
diff --git a/src/Entities/Boat.cpp b/src/Entities/Boat.cpp
index 1f7963465..4400cd4c0 100644
--- a/src/Entities/Boat.cpp
+++ b/src/Entities/Boat.cpp
@@ -34,6 +34,7 @@ cBoat::cBoat(Vector3d a_Pos, eMaterial a_Material) :
void cBoat::SpawnOn(cClientHandle & a_ClientHandle)
{
a_ClientHandle.SendSpawnEntity(*this);
+ a_ClientHandle.SendEntityMetadata(*this); // Boat colour
}
@@ -154,9 +155,6 @@ void cBoat::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
SetLastDamage(GetLastDamage() - 1);
}
-
- // Broadcast any changes in position
- m_World->BroadcastEntityMetadata(*this);
}
@@ -183,6 +181,9 @@ void cBoat::HandleSpeedFromAttachee(float a_Forward, float a_Sideways)
void cBoat::SetLastDamage(int TimeSinceLastHit)
{
m_LastDamage = TimeSinceLastHit;
+
+ // Tell the client to play the shaking animation
+ m_World->BroadcastEntityMetadata(*this);
}
@@ -191,10 +192,16 @@ void cBoat::SetLastDamage(int TimeSinceLastHit)
void cBoat::UpdatePaddles(bool a_RightPaddleUsed, bool a_LeftPaddleUsed)
{
+ // Avoid telling client what it already knows since it may reset animation 1.13+
+ const bool Changed = (m_RightPaddleUsed != a_RightPaddleUsed) || (m_LeftPaddleUsed != a_LeftPaddleUsed);
+
m_RightPaddleUsed = a_RightPaddleUsed;
m_LeftPaddleUsed = a_LeftPaddleUsed;
- m_World->BroadcastEntityMetadata(*this);
+ if (Changed)
+ {
+ m_World->BroadcastEntityMetadata(*this);
+ }
}
diff --git a/src/Protocol/Protocol.h b/src/Protocol/Protocol.h
index e1d901321..2df8103b0 100644
--- a/src/Protocol/Protocol.h
+++ b/src/Protocol/Protocol.h
@@ -145,6 +145,193 @@ public:
pktWindowProperty
};
+ enum class eEntityMetadata
+ {
+ EntityFlags,
+ EntityAir,
+ EntityCustomName,
+ EntityCustomNameVisible,
+ EntitySilent,
+ EntityNoGravity,
+ EntityPose,
+
+ PotionThrown,
+
+ FallingBlockPosition,
+
+ AreaEffectCloudRadius,
+ AreaEffectCloudColor,
+ AreaEffectCloudSinglePointEffect,
+ AreaEffectCloudParticleId,
+ AreaEffectCloudParticleParameter1,
+ AreaEffectCloudParticleParameter2,
+
+ ArrowFlags,
+ TippedArrowColor,
+
+ BoatLastHitTime,
+ BoatForwardDirection,
+ BoatDamageTaken,
+ BoatType,
+ BoatLeftPaddleTurning,
+ BoatRightPaddleTurning,
+ BoatSplashTimer,
+
+ EnderCrystalBeamTarget,
+ EnderCrystalShowBottom,
+
+ WitherSkullInvulnerable,
+
+ FireworkInfo,
+ FireworkBoostedEntityId,
+
+ ItemFrameItem,
+ ItemFrameRotation,
+
+ ItemItem,
+
+ LivingActiveHand,
+ LivingHealth,
+ LivingPotionEffectColor,
+ LivingPotionEffectAmbient,
+ LivingNumberOfArrows,
+
+ PlayerAdditionalHearts,
+ PlayerScore,
+ PlayerDisplayedSkinParts,
+ PlayerMainHand,
+
+ ArmorStandStatus,
+ ArmorStandHeadRotation,
+ ArmorStandBodyRotation,
+ ArmorStandLeftArmRotation,
+ ArmorStandRightArmRotation,
+ ArmorStandLeftLegRotation,
+ ArmorStandRightLegRotation,
+
+ InsentientFlags,
+
+ BatHanging,
+
+ AgeableIsBaby,
+
+ AbstractHorseFlags,
+ AbstractHorseOwner,
+
+ HorseVariant,
+ HorseArmour,
+
+ ChestedHorseChested,
+
+ LlamaStrength,
+ LlamaCarpetColor,
+ LlamaVariant,
+
+ PigHasSaddle,
+ PigTotalCarrotOnAStickBoost,
+
+ RabbitType,
+
+ PolarBearStanding,
+
+ SheepFlags,
+
+ TameableAnimalFlags,
+ TameableAnimalOwner,
+
+ OcelotType,
+
+ WolfDamageTaken,
+ WolfBegging,
+ WolfCollarColour,
+
+ VillagerProfession,
+
+ IronGolemPlayerCreated,
+
+ ShulkerFacingDirection,
+ ShulkerAttachmentFallingBlockPosition,
+ ShulkerShieldHeight,
+
+ BlazeOnFire,
+
+ CreeperState,
+ CreeperPowered,
+ CreeperIgnited,
+
+ GuardianStatus,
+ GuardianTarget,
+
+ IllagerFlags,
+ SpeIlagerSpell,
+
+ VexFlags,
+
+ AbstractSkeletonArmsSwinging,
+
+ SpiderClimbing,
+
+ WitchAggresive,
+
+ WitherFirstHeadTarget,
+ WitherSecondHeadTarget,
+ WitherThirdHeadTarget,
+ WitherInvulnerableTimer,
+
+ ZombieIsBaby,
+ ZombieUnusedWasType,
+ ZombieHandsRisedUp,
+
+ ZombieVillagerConverting,
+ ZombieVillagerProfession,
+
+ EndermanCarriedBlock,
+ EndermanScreaming,
+
+ EnderDragonDragonPhase,
+
+ GhastAttacking,
+
+ SlimeSize,
+
+ MinecartShakingPower,
+ MinecartShakingDirection,
+ MinecartShakingMultiplier,
+ MinecartBlockIDMeta,
+ MinecartBlockY,
+ MinecartShowBlock,
+
+ MinecartCommandBlockCommand,
+ MinecartCommandBlockLastOutput,
+
+ MinecartFurnacePowered,
+
+ TNTPrimedFuseTime
+ };
+
+ enum class eEntityMetadataType
+ {
+ Byte,
+ VarInt,
+ Float,
+ String,
+ Chat,
+ OptChat,
+ Item,
+ Boolean,
+ Rotation,
+ Position,
+ OptPosition,
+ Direction,
+ OptUUID,
+ OptBlockID,
+ NBT,
+ Particle,
+ VillagerData,
+ OptVarInt,
+ Pose
+ };
+
/** Called when client sends some data */
virtual void DataReceived(const char * a_Data, size_t a_Size) = 0;
diff --git a/src/Protocol/Protocol_1_13.cpp b/src/Protocol/Protocol_1_13.cpp
index 996111c4d..886df6649 100644
--- a/src/Protocol/Protocol_1_13.cpp
+++ b/src/Protocol/Protocol_1_13.cpp
@@ -22,6 +22,8 @@ Implements the 1.13 protocol classes:
#include "../Entities/FireworkEntity.h"
#include "../Entities/SplashPotionEntity.h"
+#include "../Mobs/IncludeAllMonsters.h"
+
#include "../BlockTypePalette.h"
#include "../ClientHandle.h"
#include "../Root.h"
@@ -93,73 +95,129 @@ void cProtocol_1_13::Initialize(cClientHandle & a_Client)
-UInt32 cProtocol_1_13::GetPacketID(ePacketType a_PacketType)
+AString cProtocol_1_13::GetPaletteVersion() const
{
- switch (a_PacketType)
+ 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)
{
- case pktAttachEntity: return 0x40;
- 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 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);
- }
+ 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[]
}
-AString cProtocol_1_13::GetPaletteVersion() const
+void cProtocol_1_13::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer)
{
- return "1.13";
+ 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
}
@@ -288,109 +346,65 @@ void cProtocol_1_13::HandlePacketPluginMessage(cByteBuffer & a_ByteBuffer)
-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)
+UInt32 cProtocol_1_13::GetPacketID(ePacketType a_PacketType)
{
- 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)
+ switch (a_PacketType)
{
- 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::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
+ 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);
+ }
}
@@ -444,6 +458,168 @@ UInt32 cProtocol_1_13::GetProtocolMobType(eMonsterType a_MobType)
+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);
@@ -507,7 +683,536 @@ void cProtocol_1_13::WriteItem(cPacketizer & a_Pkt, const cItem & a_Item)
+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)
{
- // TODO: Investigate
+ // 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())
}
diff --git a/src/Protocol/Protocol_1_13.h b/src/Protocol/Protocol_1_13.h
index 3d70f74d7..168c279c1 100644
--- a/src/Protocol/Protocol_1_13.h
+++ b/src/Protocol/Protocol_1_13.h
@@ -39,45 +39,51 @@ public:
virtual void Initialize(cClientHandle & a_Client) override;
-
protected:
- /** The palette used to transform internal block type palette into the protocol-specific ID. */
- std::shared_ptr<const BlockTypePalette> m_BlockTypePalette;
-
- /** Temporary hack for initial 1.13+ support while keeping BLOCKTYPE data:
- Map of the BLOCKTYPE#META to the protocol-specific NetBlockID. */
- std::map<UInt32, UInt32> m_BlockTypeMap;
-
-
/** Returns the string identifying the palettes' version, such as "1.13" or "1.14.4".
The palettes for that version are loaded into m_BlockTypePalette and m_ItemTypePalette. */
virtual AString GetPaletteVersion() const;
- // Outgoing packet type translation:
- virtual UInt32 GetPacketID(ePacketType a_PacketType) override;
-
- // Packet receiving:
- virtual bool HandlePacket(cByteBuffer & a_ByteBuffer, UInt32 a_PacketType) override;
- virtual void HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer) override;
- virtual void HandlePacketPluginMessage(cByteBuffer & a_ByteBuffer) override;
-
// Packet sending:
virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override;
virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override;
virtual void SendMapData (const cMap & a_Map, int a_DataStartX, int a_DataStartY) override;
virtual void SendPaintingSpawn (const cPainting & a_Painting) override;
+ virtual void SendParticleEffect (const AString & a_ParticleName, Vector3f a_Src, Vector3f a_Offset, float a_ParticleData, int a_ParticleAmount, std::array<int, 2> a_Data) override;
virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) override;
virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) override;
virtual void SendStatistics (const cStatManager & a_Manager) override;
virtual void SendTabCompletionResults (const AStringVector & a_Results) override;
virtual void SendUpdateBlockEntity (cBlockEntity & a_BlockEntity) override;
+ // Packet receiving:
+ virtual bool HandlePacket(cByteBuffer & a_ByteBuffer, UInt32 a_PacketType) override;
+ virtual void HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer) override;
+ virtual void HandlePacketPluginMessage(cByteBuffer & a_ByteBuffer) override;
+
+ // Outgoing packet type translation:
+ virtual UInt32 GetPacketID(ePacketType a_PacketType) override;
+
/** Converts eMonsterType to protocol-specific mob types */
virtual UInt32 GetProtocolMobType(eMonsterType a_MobType) override;
+ virtual UInt8 GetEntityMetadataID(eEntityMetadata a_Metadata);
+ virtual UInt8 GetEntityMetadataID(eEntityMetadataType a_FieldType);
+
virtual bool ReadItem(cByteBuffer & a_ByteBuffer, cItem & a_Item, size_t a_KeepRemainingBytes) override;
virtual void WriteItem(cPacketizer & a_Pkt, const cItem & a_Item) override;
+ virtual void WriteEntityMetadata(cPacketizer & a_Pkt, const eEntityMetadata a_Metadata, const eEntityMetadataType a_FieldType);
virtual void WriteEntityMetadata(cPacketizer & a_Pkt, const cEntity & a_Entity) override;
+ virtual void WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_Mob) override;
+
+private:
+
+ /** The palette used to transform internal block type palette into the protocol-specific ID. */
+ std::shared_ptr<const BlockTypePalette> m_BlockTypePalette;
+
+ /** Temporary hack for initial 1.13+ support while keeping BLOCKTYPE data:
+ Map of the BLOCKTYPE#META to the protocol-specific NetBlockID. */
+ std::map<UInt32, UInt32> m_BlockTypeMap;
};