36a67df105
* fixed network fixed explosion fixed ticking added network broadcast to Setter method added nullptr protection using macros in 1.10 Protocol_1_10.cpp revealed functions to LUA API small fixups, typos, less functions used fixed more doc readded info that saving is done only if the beam is displayed made the constructor transfer all needed members fixed wrong commit removed default parameters on SpawnEnderCrystal fixed wrong metadata moved call to destroy in the right place fixed some typos Fixed Ender Crystal * fixed documentation * fixed doc and added proper error message * Parameters, arrows * Parameters Co-authored-by: 12xx12 <12xx12100@gmail.com> Co-authored-by: Tiger Wang <ziwei.tiger@outlook.com>
1387 lines
44 KiB
C++
1387 lines
44 KiB
C++
|
|
// Protocol_1_13.cpp
|
|
|
|
/*
|
|
Implements the 1.13 protocol classes:
|
|
- release 1.13 protocol (#393)
|
|
- release 1.13.1 protocol (#401)
|
|
- release 1.13.2 protocol (#404)
|
|
*/
|
|
|
|
#include "Globals.h"
|
|
#include "Protocol_1_13.h"
|
|
|
|
#include "../Entities/Boat.h"
|
|
#include "../Entities/EnderCrystal.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 "../ClientHandle.h"
|
|
#include "../Root.h"
|
|
#include "../Server.h"
|
|
#include "../World.h"
|
|
#include "../JsonUtils.h"
|
|
|
|
#include "../Bindings/PluginManager.h"
|
|
|
|
#include "Palettes/Palette_1_13.h"
|
|
#include "Palettes/Palette_1_13_1.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:
|
|
|
|
void cProtocol_1_13::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
|
|
{
|
|
cPacketizer Pkt(*this, pktBlockChange);
|
|
Pkt.WriteXYZPosition64(a_BlockX, a_BlockY, a_BlockZ);
|
|
Pkt.WriteVarInt32(GetProtocolBlockType(a_BlockType, a_BlockMeta));
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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 (const auto & Change : a_Changes)
|
|
{
|
|
Int16 Coords = static_cast<Int16>(Change.m_RelY | (Change.m_RelZ << 8) | (Change.m_RelX << 12));
|
|
Pkt.WriteBEInt16(Coords);
|
|
Pkt.WriteVarInt32(GetProtocolBlockType(Change.m_BlockType, Change.m_BlockMeta));
|
|
} // for itr - a_Changes[]
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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::SendScoreboardObjective(const AString & a_Name, const AString & a_DisplayName, Byte a_Mode)
|
|
{
|
|
// TODO
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_13::SendStatistics(const cStatManager & a_Manager)
|
|
{
|
|
ASSERT(m_State == 3); // In game mode?
|
|
|
|
UInt32 Size = 0;
|
|
a_Manager.ForEachStatisticType([this, &Size](const auto & Store)
|
|
{
|
|
for (const auto & Item : Store)
|
|
{
|
|
// Client balks at out-of-range values so there is no good default value
|
|
// We're forced to not send the statistics this protocol version doesn't support
|
|
|
|
if (GetProtocolStatisticType(Item.first) != static_cast<UInt32>(-1))
|
|
{
|
|
Size++;
|
|
}
|
|
}
|
|
});
|
|
|
|
// No need to check Size != 0
|
|
// Assume that the vast majority of the time there's at least one statistic to send
|
|
|
|
cPacketizer Pkt(*this, pktStatistics);
|
|
Pkt.WriteVarInt32(Size);
|
|
|
|
a_Manager.ForEachStatisticType([this, &Pkt](const cStatManager::CustomStore & Store)
|
|
{
|
|
for (const auto & Item : Store)
|
|
{
|
|
const auto ID = GetProtocolStatisticType(Item.first);
|
|
if (ID == static_cast<UInt32>(-1))
|
|
{
|
|
// Unsupported, don't send:
|
|
continue;
|
|
}
|
|
|
|
Pkt.WriteVarInt32(8); // "Custom" category
|
|
Pkt.WriteVarInt32(ID);
|
|
Pkt.WriteVarInt32(static_cast<UInt32>(Item.second));
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_13::SendTabCompletionResults(const AStringVector & a_Results)
|
|
{
|
|
// TODO
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_13::SendUpdateBlockEntity(cBlockEntity & a_BlockEntity)
|
|
{
|
|
ASSERT(m_State == 3); // In game mode?
|
|
cPacketizer Pkt(*this, pktUpdateBlockEntity);
|
|
|
|
Pkt.WriteXYZPosition64(a_BlockEntity.GetPosX(), a_BlockEntity.GetPosY(), a_BlockEntity.GetPosZ());
|
|
|
|
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_CONDUIT: Action = 5; break; // Update Conduit entity
|
|
case E_BLOCK_STANDING_BANNER:
|
|
case E_BLOCK_WALL_BANNER: Action = 6; break; // Update banner entity
|
|
// case Structure Block: Action = 7; break; // Update Structure tile entity
|
|
case E_BLOCK_END_GATEWAY: Action = 8; break; // Update destination for a end gateway entity
|
|
case E_BLOCK_SIGN_POST: Action = 9; break; // Update sign entity
|
|
// case E_BLOCK_SHULKER_BOX: Action = 10; break; // sets shulker box - not used just here if anyone is confused from reading the protocol wiki
|
|
case E_BLOCK_BED: Action = 11; break; // Update bed color
|
|
|
|
case E_BLOCK_ENCHANTMENT_TABLE: Action = 0; break; // The ones with a action of 0 is just a workaround to send the block entities to a client.
|
|
case E_BLOCK_END_PORTAL: Action = 0; break; // Todo: 18.09.2020 - remove this when block entities are transmitted in the ChunkData packet - 12xx12
|
|
|
|
default: ASSERT(!"Unhandled or unimplemented BlockEntity update request!"); break;
|
|
}
|
|
Pkt.WriteBEUInt8(Action);
|
|
|
|
WriteBlockEntity(Pkt, a_BlockEntity);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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 0x20: HandlePacketSetBeaconEffect(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::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);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_13::HandlePacketSetBeaconEffect(cByteBuffer & a_ByteBuffer)
|
|
{
|
|
HANDLE_READ(a_ByteBuffer, ReadVarInt32, UInt32, Effect1);
|
|
HANDLE_READ(a_ByteBuffer, ReadVarInt32, UInt32, Effect2);
|
|
m_Client->HandleBeaconSelection(
|
|
static_cast<int>(Effect1), static_cast<int>(Effect2)
|
|
);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
cProtocol::Version cProtocol_1_13::GetProtocolVersion()
|
|
{
|
|
return Version::v1_13;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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(EntityMetadata 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 EntityMetadata::EntityFlags: return 0;
|
|
case EntityMetadata::EntityAir: return 1;
|
|
case EntityMetadata::EntityCustomName: return 2;
|
|
case EntityMetadata::EntityCustomNameVisible: return 3;
|
|
case EntityMetadata::EntitySilent: return 4;
|
|
case EntityMetadata::EntityNoGravity: return 5;
|
|
case EntityMetadata::PotionThrown: return Entity;
|
|
case EntityMetadata::FallingBlockPosition: return Entity;
|
|
case EntityMetadata::AreaEffectCloudRadius: return Entity;
|
|
case EntityMetadata::AreaEffectCloudColor: return Entity + 1;
|
|
case EntityMetadata::AreaEffectCloudSinglePointEffect: return Entity + 2;
|
|
case EntityMetadata::AreaEffectCloudParticleId: return Entity + 3;
|
|
case EntityMetadata::ArrowFlags: return Entity;
|
|
case EntityMetadata::TippedArrowColor: return Entity + 1;
|
|
case EntityMetadata::BoatLastHitTime: return Entity;
|
|
case EntityMetadata::BoatForwardDirection: return Entity + 1;
|
|
case EntityMetadata::BoatDamageTaken: return Entity + 2;
|
|
case EntityMetadata::BoatType: return Entity + 3;
|
|
case EntityMetadata::BoatLeftPaddleTurning: return Entity + 4;
|
|
case EntityMetadata::BoatRightPaddleTurning: return Entity + 5;
|
|
case EntityMetadata::BoatSplashTimer: return Entity + 6;
|
|
case EntityMetadata::EnderCrystalBeamTarget: return Entity;
|
|
case EntityMetadata::EnderCrystalShowBottom: return Entity + 1;
|
|
case EntityMetadata::WitherSkullInvulnerable: return Entity;
|
|
case EntityMetadata::FireworkInfo: return Entity;
|
|
case EntityMetadata::FireworkBoostedEntityId: return Entity + 1;
|
|
case EntityMetadata::ItemFrameItem: return Entity;
|
|
case EntityMetadata::ItemFrameRotation: return Entity + 1;
|
|
case EntityMetadata::ItemItem: return Entity;
|
|
case EntityMetadata::LivingActiveHand: return Entity;
|
|
case EntityMetadata::LivingHealth: return Entity + 1;
|
|
case EntityMetadata::LivingPotionEffectColor: return Entity + 2;
|
|
case EntityMetadata::LivingPotionEffectAmbient: return Entity + 3;
|
|
case EntityMetadata::LivingNumberOfArrows: return Entity + 4;
|
|
case EntityMetadata::PlayerAdditionalHearts: return Living;
|
|
case EntityMetadata::PlayerScore: return Living + 1;
|
|
case EntityMetadata::PlayerDisplayedSkinParts: return Living + 2;
|
|
case EntityMetadata::PlayerMainHand: return Living + 3;
|
|
case EntityMetadata::ArmorStandStatus: return Living;
|
|
case EntityMetadata::ArmorStandHeadRotation: return Living + 1;
|
|
case EntityMetadata::ArmorStandBodyRotation: return Living + 2;
|
|
case EntityMetadata::ArmorStandLeftArmRotation: return Living + 3;
|
|
case EntityMetadata::ArmorStandRightArmRotation: return Living + 4;
|
|
case EntityMetadata::ArmorStandLeftLegRotation: return Living + 5;
|
|
case EntityMetadata::ArmorStandRightLegRotation: return Living + 6;
|
|
case EntityMetadata::InsentientFlags: return Living;
|
|
case EntityMetadata::BatHanging: return Insentient;
|
|
case EntityMetadata::AgeableIsBaby: return Insentient;
|
|
case EntityMetadata::AbstractHorseFlags: return Ageable;
|
|
case EntityMetadata::AbstractHorseOwner: return Ageable + 1;
|
|
case EntityMetadata::HorseVariant: return AbstractHorse;
|
|
case EntityMetadata::HorseArmour: return AbstractHorse + 1;
|
|
case EntityMetadata::ChestedHorseChested: return AbstractHorse;
|
|
case EntityMetadata::LlamaStrength: return ChestedHorse;
|
|
case EntityMetadata::LlamaCarpetColor: return ChestedHorse + 1;
|
|
case EntityMetadata::LlamaVariant: return ChestedHorse + 2;
|
|
case EntityMetadata::PigHasSaddle: return Ageable;
|
|
case EntityMetadata::PigTotalCarrotOnAStickBoost: return Ageable + 1;
|
|
case EntityMetadata::RabbitType: return Ageable;
|
|
case EntityMetadata::PolarBearStanding: return Ageable;
|
|
case EntityMetadata::SheepFlags: return Ageable;
|
|
case EntityMetadata::TameableAnimalFlags: return Ageable;
|
|
case EntityMetadata::TameableAnimalOwner: return Ageable + 1;
|
|
case EntityMetadata::OcelotType: return TameableAnimal;
|
|
case EntityMetadata::WolfDamageTaken: return TameableAnimal;
|
|
case EntityMetadata::WolfBegging: return TameableAnimal + 1;
|
|
case EntityMetadata::WolfCollarColour: return TameableAnimal + 2;
|
|
case EntityMetadata::VillagerProfession: return Ageable;
|
|
case EntityMetadata::IronGolemPlayerCreated: return Insentient;
|
|
case EntityMetadata::ShulkerFacingDirection: return Insentient;
|
|
case EntityMetadata::ShulkerAttachmentFallingBlockPosition: return Insentient + 1;
|
|
case EntityMetadata::ShulkerShieldHeight: return Insentient + 2;
|
|
case EntityMetadata::BlazeOnFire: return Insentient;
|
|
case EntityMetadata::CreeperState: return Insentient;
|
|
case EntityMetadata::CreeperPowered: return Insentient + 1;
|
|
case EntityMetadata::CreeperIgnited: return Insentient + 2;
|
|
case EntityMetadata::GuardianStatus: return Insentient;
|
|
case EntityMetadata::GuardianTarget: return Insentient + 1;
|
|
case EntityMetadata::IllagerFlags: return Insentient;
|
|
case EntityMetadata::SpeIlagerSpell: return Insentient + 1;
|
|
case EntityMetadata::VexFlags: return Insentient;
|
|
case EntityMetadata::SpiderClimbing: return Insentient;
|
|
case EntityMetadata::WitchAggresive: return Insentient;
|
|
case EntityMetadata::WitherFirstHeadTarget: return Insentient;
|
|
case EntityMetadata::WitherSecondHeadTarget: return Insentient + 1;
|
|
case EntityMetadata::WitherThirdHeadTarget: return Insentient + 2;
|
|
case EntityMetadata::WitherInvulnerableTimer: return Insentient + 3;
|
|
case EntityMetadata::ZombieIsBaby: return Insentient;
|
|
case EntityMetadata::ZombieHandsRisedUp: return Insentient + 2;
|
|
case EntityMetadata::ZombieVillagerConverting: return Insentient + 4;
|
|
case EntityMetadata::ZombieVillagerProfession: return Insentient + 5;
|
|
case EntityMetadata::EndermanCarriedBlock: return Insentient;
|
|
case EntityMetadata::EndermanScreaming: return Insentient + 1;
|
|
case EntityMetadata::EnderDragonDragonPhase: return Insentient;
|
|
case EntityMetadata::GhastAttacking: return Insentient;
|
|
case EntityMetadata::SlimeSize: return Insentient;
|
|
case EntityMetadata::MinecartShakingPower: return Entity;
|
|
case EntityMetadata::MinecartShakingDirection: return Entity + 1;
|
|
case EntityMetadata::MinecartShakingMultiplier: return Entity + 2;
|
|
case EntityMetadata::MinecartBlockIDMeta: return Entity + 3;
|
|
case EntityMetadata::MinecartBlockY: return Entity + 4;
|
|
case EntityMetadata::MinecartShowBlock: return Entity + 5;
|
|
case EntityMetadata::MinecartCommandBlockCommand: return Minecart;
|
|
case EntityMetadata::MinecartCommandBlockLastOutput: return Minecart + 1;
|
|
case EntityMetadata::MinecartFurnacePowered: return Minecart;
|
|
case EntityMetadata::TNTPrimedFuseTime: return Entity;
|
|
|
|
case EntityMetadata::EntityPose:
|
|
case EntityMetadata::AreaEffectCloudParticleParameter1:
|
|
case EntityMetadata::AreaEffectCloudParticleParameter2:
|
|
case EntityMetadata::AbstractSkeletonArmsSwinging:
|
|
case EntityMetadata::ZombieUnusedWasType: break;
|
|
}
|
|
|
|
UNREACHABLE("Retrieved invalid metadata for protocol");
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
UInt8 cProtocol_1_13::GetEntityMetadataID(EntityMetadataType a_FieldType)
|
|
{
|
|
switch (a_FieldType)
|
|
{
|
|
case EntityMetadataType::Byte: return 0;
|
|
case EntityMetadataType::VarInt: return 1;
|
|
case EntityMetadataType::Float: return 2;
|
|
case EntityMetadataType::String: return 3;
|
|
case EntityMetadataType::Chat: return 4;
|
|
case EntityMetadataType::OptChat: return 5;
|
|
case EntityMetadataType::Item: return 6;
|
|
case EntityMetadataType::Boolean: return 7;
|
|
case EntityMetadataType::Rotation: return 8;
|
|
case EntityMetadataType::Position: return 9;
|
|
case EntityMetadataType::OptPosition: return 10;
|
|
case EntityMetadataType::Direction: return 11;
|
|
case EntityMetadataType::OptUUID: return 12;
|
|
case EntityMetadataType::OptBlockID: return 13;
|
|
case EntityMetadataType::NBT: return 14;
|
|
case EntityMetadataType::Particle: return 15;
|
|
case EntityMetadataType::VillagerData: return 16;
|
|
case EntityMetadataType::OptVarInt: return 17;
|
|
case EntityMetadataType::Pose: return 18;
|
|
}
|
|
|
|
UNREACHABLE("Translated invalid metadata type for protocol");
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
std::pair<short, short> cProtocol_1_13::GetItemFromProtocolID(UInt32 a_ProtocolID)
|
|
{
|
|
return PaletteUpgrade::ToItem(Palette_1_13::ToItem(a_ProtocolID));
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
UInt32 cProtocol_1_13::GetProtocolBlockType(BLOCKTYPE a_BlockType, NIBBLETYPE a_Meta)
|
|
{
|
|
return Palette_1_13::FromBlock(PaletteUpgrade::FromBlock(a_BlockType, a_Meta));
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
UInt32 cProtocol_1_13::GetProtocolItemType(short a_ItemID, short a_ItemDamage)
|
|
{
|
|
return Palette_1_13::FromItem(PaletteUpgrade::FromItem(a_ItemID, a_ItemDamage));
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
UInt32 cProtocol_1_13::GetProtocolStatisticType(Statistic a_Statistic)
|
|
{
|
|
return Palette_1_13::From(a_Statistic);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cProtocol_1_13::ReadItem(cByteBuffer & a_ByteBuffer, cItem & a_Item, size_t a_KeepRemainingBytes)
|
|
{
|
|
HANDLE_PACKET_READ(a_ByteBuffer, ReadBEInt16, Int16, ItemID);
|
|
if (ItemID == -1)
|
|
{
|
|
// The item is empty, no more data follows
|
|
a_Item.Empty();
|
|
return true;
|
|
}
|
|
|
|
const auto Translated = GetItemFromProtocolID(ToUnsigned(ItemID));
|
|
a_Item.m_ItemType = Translated.first;
|
|
a_Item.m_ItemDamage = Translated.second;
|
|
|
|
HANDLE_PACKET_READ(a_ByteBuffer, ReadBEInt8, Int8, ItemCount);
|
|
a_Item.m_ItemCount = ItemCount;
|
|
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
|
|
a_Pkt.WriteBEInt16(static_cast<Int16>(GetProtocolItemType(a_Item.m_ItemType, a_Item.m_ItemDamage)));
|
|
a_Pkt.WriteBEInt8(a_Item.m_ItemCount);
|
|
|
|
// TODO: NBT
|
|
a_Pkt.WriteBEInt8(0);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_13::WriteEntityMetadata(cPacketizer & a_Pkt, const EntityMetadata a_Metadata, const EntityMetadataType 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, EntityMetadata::EntityFlags, EntityMetadataType::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, EntityMetadata::EntityCustomName, EntityMetadataType::String);
|
|
a_Pkt.WriteString(Player.GetName());
|
|
|
|
WriteEntityMetadata(a_Pkt, EntityMetadata::LivingHealth, EntityMetadataType::Float);
|
|
a_Pkt.WriteBEFloat(static_cast<float>(Player.GetHealth()));
|
|
|
|
WriteEntityMetadata(a_Pkt, EntityMetadata::PlayerDisplayedSkinParts, EntityMetadataType::Byte);
|
|
a_Pkt.WriteBEUInt8(static_cast<UInt8>(Player.GetSkinParts()));
|
|
|
|
WriteEntityMetadata(a_Pkt, EntityMetadata::PlayerMainHand, EntityMetadataType::Byte);
|
|
a_Pkt.WriteBEUInt8(static_cast<UInt8>(Player.GetMainHand()));
|
|
break;
|
|
}
|
|
case cEntity::etPickup:
|
|
{
|
|
WriteEntityMetadata(a_Pkt, EntityMetadata::ItemItem, EntityMetadataType::Item);
|
|
WriteItem(a_Pkt, static_cast<const cPickup &>(a_Entity).GetItem());
|
|
break;
|
|
}
|
|
case cEntity::etMinecart:
|
|
{
|
|
WriteEntityMetadata(a_Pkt, EntityMetadata::MinecartShakingPower, EntityMetadataType::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, EntityMetadata::MinecartShakingDirection, EntityMetadataType::VarInt);
|
|
a_Pkt.WriteVarInt32(1); // (doesn't seem to effect anything)
|
|
|
|
WriteEntityMetadata(a_Pkt, EntityMetadata::MinecartShakingMultiplier, EntityMetadataType::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, EntityMetadata::MinecartBlockIDMeta, EntityMetadataType::VarInt);
|
|
int Content = MinecartContent.m_ItemType;
|
|
Content |= MinecartContent.m_ItemDamage << 8;
|
|
a_Pkt.WriteVarInt32(static_cast<UInt32>(Content));
|
|
|
|
WriteEntityMetadata(a_Pkt, EntityMetadata::MinecartBlockY, EntityMetadataType::VarInt);
|
|
a_Pkt.WriteVarInt32(static_cast<UInt32>(RideableMinecart.GetBlockHeight()));
|
|
|
|
WriteEntityMetadata(a_Pkt, EntityMetadata::MinecartShowBlock, EntityMetadataType::Boolean);
|
|
a_Pkt.WriteBool(true);
|
|
}
|
|
}
|
|
else if (Minecart.GetPayload() == cMinecart::mpFurnace)
|
|
{
|
|
WriteEntityMetadata(a_Pkt, EntityMetadata::MinecartFurnacePowered, EntityMetadataType::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, EntityMetadata::ArrowFlags, EntityMetadataType::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, EntityMetadata::BoatLastHitTime, EntityMetadataType::VarInt);
|
|
a_Pkt.WriteVarInt32(static_cast<UInt32>(Boat.GetLastDamage()));
|
|
|
|
WriteEntityMetadata(a_Pkt, EntityMetadata::BoatForwardDirection, EntityMetadataType::VarInt);
|
|
a_Pkt.WriteVarInt32(static_cast<UInt32>(Boat.GetForwardDirection()));
|
|
|
|
WriteEntityMetadata(a_Pkt, EntityMetadata::BoatDamageTaken, EntityMetadataType::Float);
|
|
a_Pkt.WriteBEFloat(Boat.GetDamageTaken());
|
|
|
|
WriteEntityMetadata(a_Pkt, EntityMetadata::BoatType, EntityMetadataType::VarInt);
|
|
a_Pkt.WriteVarInt32(static_cast<UInt32>(Boat.GetMaterial()));
|
|
|
|
WriteEntityMetadata(a_Pkt, EntityMetadata::BoatRightPaddleTurning, EntityMetadataType::Boolean);
|
|
a_Pkt.WriteBool(Boat.IsRightPaddleUsed());
|
|
|
|
WriteEntityMetadata(a_Pkt, EntityMetadata::BoatLeftPaddleTurning, EntityMetadataType::Boolean);
|
|
a_Pkt.WriteBool(static_cast<bool>(Boat.IsLeftPaddleUsed()));
|
|
|
|
WriteEntityMetadata(a_Pkt, EntityMetadata::BoatSplashTimer, EntityMetadataType::VarInt);
|
|
a_Pkt.WriteVarInt32(0);
|
|
|
|
break;
|
|
} // case etBoat
|
|
|
|
case cEntity::etItemFrame:
|
|
{
|
|
// TODO
|
|
break;
|
|
} // case etItemFrame
|
|
|
|
case cEntity::etEnderCrystal:
|
|
{
|
|
const auto & EnderCrystal = static_cast<const cEnderCrystal &>(a_Entity);
|
|
if (EnderCrystal.DisplaysBeam())
|
|
{
|
|
WriteEntityMetadata(a_Pkt, EntityMetadata::EnderCrystalBeamTarget, EntityMetadataType::OptPosition);
|
|
a_Pkt.WriteBool(true); // Dont do a second check if it should display the beam
|
|
a_Pkt.WriteXYZPosition64(EnderCrystal.GetBeamTarget());
|
|
}
|
|
WriteEntityMetadata(a_Pkt, EntityMetadata::EnderCrystalShowBottom, EntityMetadataType::Boolean);
|
|
a_Pkt.WriteBool(EnderCrystal.ShowsBottom());
|
|
break;
|
|
} // case etEnderCrystal
|
|
|
|
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, EntityMetadata::EntityCustomName, EntityMetadataType::OptChat);
|
|
a_Pkt.WriteBool(true);
|
|
a_Pkt.WriteString(a_Mob.GetCustomName());
|
|
|
|
WriteEntityMetadata(a_Pkt, EntityMetadata::EntityCustomNameVisible, EntityMetadataType::Boolean);
|
|
a_Pkt.WriteBool(a_Mob.IsCustomNameAlwaysVisible());
|
|
}
|
|
|
|
WriteEntityMetadata(a_Pkt, EntityMetadata::LivingHealth, EntityMetadataType::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, EntityMetadata::BatHanging, EntityMetadataType::Byte);
|
|
a_Pkt.WriteBEInt8(Bat.IsHanging() ? 1 : 0);
|
|
break;
|
|
} // case mtBat
|
|
|
|
case mtChicken:
|
|
{
|
|
auto & Chicken = static_cast<const cChicken &>(a_Mob);
|
|
|
|
WriteEntityMetadata(a_Pkt, EntityMetadata::AgeableIsBaby, EntityMetadataType::Boolean);
|
|
a_Pkt.WriteBool(Chicken.IsBaby());
|
|
break;
|
|
} // case mtChicken
|
|
|
|
case mtCow:
|
|
{
|
|
auto & Cow = static_cast<const cCow &>(a_Mob);
|
|
|
|
WriteEntityMetadata(a_Pkt, EntityMetadata::AgeableIsBaby, EntityMetadataType::Boolean);
|
|
a_Pkt.WriteBool(Cow.IsBaby());
|
|
break;
|
|
} // case mtCow
|
|
|
|
case mtCreeper:
|
|
{
|
|
auto & Creeper = static_cast<const cCreeper &>(a_Mob);
|
|
|
|
WriteEntityMetadata(a_Pkt, EntityMetadata::CreeperState, EntityMetadataType::VarInt);
|
|
a_Pkt.WriteVarInt32(Creeper.IsBlowing() ? 1 : static_cast<UInt32>(-1)); // (idle or "blowing")
|
|
|
|
WriteEntityMetadata(a_Pkt, EntityMetadata::CreeperPowered, EntityMetadataType::Boolean);
|
|
a_Pkt.WriteBool(Creeper.IsCharged());
|
|
|
|
WriteEntityMetadata(a_Pkt, EntityMetadata::CreeperIgnited, EntityMetadataType::Boolean);
|
|
a_Pkt.WriteBool(Creeper.IsBurnedWithFlintAndSteel());
|
|
break;
|
|
} // case mtCreeper
|
|
|
|
case mtEnderman:
|
|
{
|
|
auto & Enderman = static_cast<const cEnderman &>(a_Mob);
|
|
WriteEntityMetadata(a_Pkt, EntityMetadata::EndermanCarriedBlock, EntityMetadataType::OptBlockID);
|
|
UInt32 Carried = 0;
|
|
Carried |= static_cast<UInt32>(Enderman.GetCarriedBlock() << 4);
|
|
Carried |= Enderman.GetCarriedMeta();
|
|
a_Pkt.WriteVarInt32(Carried);
|
|
|
|
WriteEntityMetadata(a_Pkt, EntityMetadata::EndermanScreaming, EntityMetadataType::Boolean);
|
|
a_Pkt.WriteBool(Enderman.IsScreaming());
|
|
break;
|
|
} // case mtEnderman
|
|
|
|
case mtGhast:
|
|
{
|
|
auto & Ghast = static_cast<const cGhast &>(a_Mob);
|
|
|
|
WriteEntityMetadata(a_Pkt, EntityMetadata::GhastAttacking, EntityMetadataType::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, EntityMetadata::AbstractHorseFlags, EntityMetadataType::Byte);
|
|
a_Pkt.WriteBEInt8(Flags);
|
|
|
|
// Regular horses
|
|
int Appearance = 0;
|
|
Appearance = Horse.GetHorseColor();
|
|
Appearance |= Horse.GetHorseStyle() << 8;
|
|
WriteEntityMetadata(a_Pkt, EntityMetadata::HorseVariant, EntityMetadataType::VarInt);
|
|
a_Pkt.WriteVarInt32(static_cast<UInt32>(Appearance)); // Color / style
|
|
|
|
WriteEntityMetadata(a_Pkt, EntityMetadata::HorseArmour, EntityMetadataType::VarInt);
|
|
a_Pkt.WriteVarInt32(static_cast<UInt32>(Horse.GetHorseArmour()));
|
|
|
|
WriteEntityMetadata(a_Pkt, EntityMetadata::AgeableIsBaby, EntityMetadataType::Boolean);
|
|
a_Pkt.WriteBool(Horse.IsBaby());
|
|
break;
|
|
} // case mtHorse
|
|
|
|
case mtMagmaCube:
|
|
{
|
|
auto & MagmaCube = static_cast<const cMagmaCube &>(a_Mob);
|
|
|
|
WriteEntityMetadata(a_Pkt, EntityMetadata::SlimeSize, EntityMetadataType::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, EntityMetadata::AgeableIsBaby, EntityMetadataType::Boolean);
|
|
a_Pkt.WriteBool(Ocelot.IsBaby());
|
|
|
|
Int8 OcelotStatus = 0;
|
|
if (Ocelot.IsSitting())
|
|
{
|
|
OcelotStatus |= 0x1;
|
|
}
|
|
if (Ocelot.IsTame())
|
|
{
|
|
OcelotStatus |= 0x4;
|
|
}
|
|
WriteEntityMetadata(a_Pkt, EntityMetadata::TameableAnimalFlags, EntityMetadataType::Byte);
|
|
a_Pkt.WriteBEInt8(OcelotStatus);
|
|
|
|
WriteEntityMetadata(a_Pkt, EntityMetadata::OcelotType, EntityMetadataType::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, EntityMetadata::AgeableIsBaby, EntityMetadataType::Boolean);
|
|
a_Pkt.WriteBool(Pig.IsBaby());
|
|
|
|
WriteEntityMetadata(a_Pkt, EntityMetadata::PigHasSaddle, EntityMetadataType::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, EntityMetadata::AgeableIsBaby, EntityMetadataType::Boolean);
|
|
a_Pkt.WriteBool(Rabbit.IsBaby());
|
|
|
|
WriteEntityMetadata(a_Pkt, EntityMetadata::RabbitType, EntityMetadataType::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, EntityMetadata::AgeableIsBaby, EntityMetadataType::Boolean);
|
|
a_Pkt.WriteBool(Sheep.IsBaby());
|
|
|
|
Int8 SheepMetadata = 0;
|
|
SheepMetadata = static_cast<Int8>(Sheep.GetFurColor());
|
|
if (Sheep.IsSheared())
|
|
{
|
|
SheepMetadata |= 0x10;
|
|
}
|
|
WriteEntityMetadata(a_Pkt, EntityMetadata::SheepFlags, EntityMetadataType::Byte);
|
|
a_Pkt.WriteBEInt8(SheepMetadata);
|
|
break;
|
|
} // case mtSheep
|
|
|
|
case mtSlime:
|
|
{
|
|
auto & Slime = static_cast<const cSlime &>(a_Mob);
|
|
|
|
WriteEntityMetadata(a_Pkt, EntityMetadata::SlimeSize, EntityMetadataType::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, EntityMetadata::AgeableIsBaby, EntityMetadataType::Boolean);
|
|
a_Pkt.WriteBool(Villager.IsBaby());
|
|
|
|
WriteEntityMetadata(a_Pkt, EntityMetadata::VillagerProfession, EntityMetadataType::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, EntityMetadata::WitchAggresive, EntityMetadataType::Boolean);
|
|
a_Pkt.WriteBool(Witch.IsAngry());
|
|
break;
|
|
} // case mtWitch
|
|
|
|
case mtWither:
|
|
{
|
|
auto & Wither = static_cast<const cWither &>(a_Mob);
|
|
|
|
WriteEntityMetadata(a_Pkt, EntityMetadata::WitherInvulnerableTimer, EntityMetadataType::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, EntityMetadata::AgeableIsBaby, EntityMetadataType::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, EntityMetadata::TameableAnimalFlags, EntityMetadataType::Byte);
|
|
a_Pkt.WriteBEInt8(WolfStatus);
|
|
|
|
WriteEntityMetadata(a_Pkt, EntityMetadata::WolfDamageTaken, EntityMetadataType::Float);
|
|
a_Pkt.WriteBEFloat(static_cast<float>(a_Mob.GetHealth())); // TODO Not use the current health
|
|
|
|
WriteEntityMetadata(a_Pkt, EntityMetadata::WolfBegging, EntityMetadataType::Boolean);
|
|
a_Pkt.WriteBool(Wolf.IsBegging());
|
|
|
|
WriteEntityMetadata(a_Pkt, EntityMetadata::WolfCollarColour, EntityMetadataType::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, EntityMetadata::ZombieIsBaby, EntityMetadataType::Boolean);
|
|
a_Pkt.WriteBool(Zombie.IsBaby());
|
|
break;
|
|
} // case mtZombie
|
|
|
|
case mtZombiePigman:
|
|
{
|
|
auto & ZombiePigman = static_cast<const cZombiePigman &>(a_Mob);
|
|
|
|
WriteEntityMetadata(a_Pkt, EntityMetadata::AgeableIsBaby, EntityMetadataType::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())
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// cProtocol_1_13_1:
|
|
|
|
cProtocol::Version cProtocol_1_13_1::GetProtocolVersion()
|
|
{
|
|
return Version::v1_13_1;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
std::pair<short, short> cProtocol_1_13_1::GetItemFromProtocolID(UInt32 a_ProtocolID)
|
|
{
|
|
return PaletteUpgrade::ToItem(Palette_1_13_1::ToItem(a_ProtocolID));
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
UInt32 cProtocol_1_13_1::GetProtocolBlockType(BLOCKTYPE a_BlockType, NIBBLETYPE a_Meta)
|
|
{
|
|
return Palette_1_13_1::FromBlock(PaletteUpgrade::FromBlock(a_BlockType, a_Meta));
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
UInt32 cProtocol_1_13_1::GetProtocolItemType(short a_ItemID, short a_ItemDamage)
|
|
{
|
|
return Palette_1_13_1::FromItem(PaletteUpgrade::FromItem(a_ItemID, a_ItemDamage));
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
UInt32 cProtocol_1_13_1::GetProtocolStatisticType(Statistic a_Statistic)
|
|
{
|
|
return Palette_1_13_1::From(a_Statistic);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// cProtocol_1_13_2:
|
|
|
|
cProtocol::Version cProtocol_1_13_2::GetProtocolVersion()
|
|
{
|
|
return Version::v1_13_2;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cProtocol_1_13_2::ReadItem(cByteBuffer & a_ByteBuffer, cItem & a_Item, size_t a_KeepRemainingBytes)
|
|
{
|
|
HANDLE_PACKET_READ(a_ByteBuffer, ReadBool, bool, Present);
|
|
if (!Present)
|
|
{
|
|
// The item is empty, no more data follows
|
|
a_Item.Empty();
|
|
return true;
|
|
}
|
|
|
|
HANDLE_PACKET_READ(a_ByteBuffer, ReadVarInt32, UInt32, ItemID);
|
|
const auto Translated = GetItemFromProtocolID(ItemID);
|
|
a_Item.m_ItemType = Translated.first;
|
|
a_Item.m_ItemDamage = Translated.second;
|
|
|
|
HANDLE_PACKET_READ(a_ByteBuffer, ReadBEInt8, Int8, ItemCount);
|
|
a_Item.m_ItemCount = ItemCount;
|
|
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_2::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.WriteBool(false);
|
|
return;
|
|
}
|
|
|
|
// Item present
|
|
a_Pkt.WriteBool(true);
|
|
|
|
// Normal item
|
|
a_Pkt.WriteVarInt32(GetProtocolItemType(a_Item.m_ItemType, a_Item.m_ItemDamage));
|
|
a_Pkt.WriteBEInt8(a_Item.m_ItemCount);
|
|
|
|
// TODO: NBT
|
|
a_Pkt.WriteBEInt8(0);
|
|
}
|