2017-06-14 05:22:51 -04:00
|
|
|
|
|
|
|
// Protocol_1_12.cpp
|
|
|
|
|
|
|
|
/*
|
|
|
|
Implements the 1.12 protocol classes:
|
|
|
|
- release 1.12 protocol (#335)
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "Globals.h"
|
|
|
|
#include "Protocol_1_12.h"
|
|
|
|
#include "ProtocolRecognizer.h"
|
|
|
|
#include "Packetizer.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 "../Root.h"
|
|
|
|
#include "../Server.h"
|
|
|
|
#include "../ClientHandle.h"
|
|
|
|
#include "../Bindings/PluginManager.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// The disabled error is intended, since the Metadata have overlapping indexes
|
|
|
|
// based on the type of the Entity.
|
|
|
|
//
|
|
|
|
// IMPORTANT: The enum is used to automate the sequential counting of the
|
|
|
|
// Metadata indexes. Adding a new enum value causes the following values to
|
|
|
|
// increase their index. Therefore the ordering of the enum values is VERY important!
|
|
|
|
#ifdef __clang__
|
|
|
|
#pragma clang diagnostic push
|
|
|
|
#pragma clang diagnostic ignored "-Wduplicate-enum"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
namespace Metadata
|
|
|
|
{
|
|
|
|
enum Metadata_Index
|
|
|
|
{
|
|
|
|
// Entity
|
|
|
|
ENTITY_FLAGS,
|
|
|
|
ENTITY_AIR,
|
|
|
|
ENTITY_CUSTOM_NAME,
|
|
|
|
ENTITY_CUSTOM_NAME_VISIBLE,
|
|
|
|
ENTITY_SILENT,
|
|
|
|
ENTITY_NO_GRAVITY,
|
|
|
|
_ENTITY_NEXT, // Used by descendants
|
|
|
|
|
|
|
|
// Potion
|
|
|
|
POTION_THROWN = _ENTITY_NEXT,
|
|
|
|
|
|
|
|
// FallingBlock
|
|
|
|
FALLING_BLOCK_POSITION = _ENTITY_NEXT,
|
|
|
|
|
|
|
|
// AreaEffectCloud
|
|
|
|
AREA_EFFECT_CLOUD_RADIUS = _ENTITY_NEXT,
|
|
|
|
AREA_EFFECT_CLOUD_COLOR,
|
|
|
|
AREA_EFFECT_CLOUD_SINGLE_POINT_EFFECT,
|
|
|
|
AREA_EFFECT_CLOUD_PARTICLE_ID,
|
|
|
|
AREA_EFFECT_CLOUD_PARTICLE_PARAMETER1,
|
|
|
|
AREA_EFFECT_CLOUD_PARTICLE_PARAMETER2,
|
|
|
|
|
|
|
|
// Arrow
|
|
|
|
ARROW_CRITICAL = _ENTITY_NEXT,
|
|
|
|
_ARROW_NEXT,
|
|
|
|
|
|
|
|
// TippedArrow
|
|
|
|
TIPPED_ARROW_COLOR = _ARROW_NEXT,
|
|
|
|
|
|
|
|
// Boat
|
|
|
|
BOAT_LAST_HIT_TIME = _ENTITY_NEXT,
|
|
|
|
BOAT_FORWARD_DIRECTION,
|
|
|
|
BOAT_DAMAGE_TAKEN,
|
|
|
|
BOAT_TYPE,
|
|
|
|
BOAT_RIGHT_PADDLE_TURNING,
|
|
|
|
BOAT_LEFT_PADDLE_TURNING,
|
|
|
|
|
|
|
|
// EnderCrystal
|
|
|
|
ENDER_CRYSTAL_BEAM_TARGET = _ENTITY_NEXT,
|
|
|
|
ENDER_CRYSTAL_SHOW_BOTTOM,
|
|
|
|
|
|
|
|
// Fireball
|
|
|
|
_FIREBALL_NEXT = _ENTITY_NEXT,
|
|
|
|
|
|
|
|
// WitherSkull
|
|
|
|
WITHER_SKULL_INVULNERABLE = _FIREBALL_NEXT,
|
|
|
|
|
|
|
|
// Fireworks
|
|
|
|
FIREWORK_INFO = _ENTITY_NEXT,
|
|
|
|
FIREWORK_BOOSTED_ENTITY_ID, // 1.11.1 only
|
|
|
|
|
|
|
|
// Hanging
|
|
|
|
_HANGING_NEXT = _ENTITY_NEXT,
|
|
|
|
|
|
|
|
// ItemFrame
|
|
|
|
ITEM_FRAME_ITEM = _HANGING_NEXT,
|
|
|
|
ITEM_FRAME_ROTATION,
|
|
|
|
|
|
|
|
// Item
|
|
|
|
ITEM_ITEM = _ENTITY_NEXT,
|
|
|
|
|
|
|
|
// Living
|
|
|
|
LIVING_ACTIVE_HAND = _ENTITY_NEXT,
|
|
|
|
LIVING_HEALTH,
|
|
|
|
LIVING_POTION_EFFECT_COLOR,
|
|
|
|
LIVING_POTION_EFFECT_AMBIENT,
|
|
|
|
LIVING_NUMBER_OF_ARROWS,
|
|
|
|
_LIVING_NEXT,
|
|
|
|
|
|
|
|
// Player
|
|
|
|
PLAYER_ADDITIONAL_HEARTHS = _LIVING_NEXT,
|
|
|
|
PLAYER_SCORE,
|
|
|
|
PLAYER_DISPLAYED_SKIN_PARTS,
|
|
|
|
PLAYER_MAIN_HAND,
|
|
|
|
|
|
|
|
// ArmorStand
|
|
|
|
ARMOR_STAND_STATUS = _LIVING_NEXT,
|
|
|
|
ARMOR_STAND_HEAD_ROTATION,
|
|
|
|
ARMOR_STAND_BODY_ROTATION,
|
|
|
|
ARMOR_STAND_LEFT_ARM_ROTATION,
|
|
|
|
ARMOR_STAND_RIGHT_ARM_ROTATION,
|
|
|
|
ARMOR_STAND_LEFT_LEG_ROTATION,
|
|
|
|
ARMOR_STAND_RIGHT_LEG_ROTATION,
|
|
|
|
|
|
|
|
// Insentient
|
|
|
|
INSENTIENT_STATUS = _LIVING_NEXT,
|
|
|
|
_INSENTIENT_NEXT,
|
|
|
|
|
|
|
|
// Ambient
|
|
|
|
_AMBIENT_NEXT = _INSENTIENT_NEXT,
|
|
|
|
|
|
|
|
// Bat
|
|
|
|
BAT_HANGING = _AMBIENT_NEXT,
|
|
|
|
|
|
|
|
// Creature
|
|
|
|
_CREATURE_NEXT = _INSENTIENT_NEXT,
|
|
|
|
|
|
|
|
// Ageable
|
|
|
|
AGEABLE_BABY = _CREATURE_NEXT,
|
|
|
|
_AGEABLE_NEXT,
|
|
|
|
|
|
|
|
// PolarBear
|
|
|
|
POLAR_BEAR_STANDING = _AGEABLE_NEXT,
|
|
|
|
|
|
|
|
// Animal
|
|
|
|
_ANIMAL_NEXT = _AGEABLE_NEXT,
|
|
|
|
|
|
|
|
// Abstract horse
|
|
|
|
ABSTRACT_HORSE_STATUS = _ANIMAL_NEXT,
|
|
|
|
ABSTRACT_HORSE_OWNER,
|
|
|
|
_ABSTRACT_HORSE_NEXT,
|
|
|
|
|
|
|
|
// Horse
|
|
|
|
HORSE_VARIANT = _ABSTRACT_HORSE_NEXT,
|
|
|
|
HORSE_ARMOR,
|
|
|
|
|
|
|
|
// Chested horse
|
|
|
|
CHESTED_HORSE_CHESTED = _ABSTRACT_HORSE_NEXT,
|
|
|
|
_CHESTED_HORSE_NEXT,
|
|
|
|
|
|
|
|
// Llama
|
|
|
|
LLAMA_STRENGTH = _CHESTED_HORSE_NEXT,
|
|
|
|
LLAMA_CARPET_COLOR,
|
|
|
|
LLAMA_VARIANT,
|
|
|
|
|
|
|
|
// Pig
|
|
|
|
PIG_HAS_SADDLE = _ANIMAL_NEXT,
|
|
|
|
PIG_TOTAL_CARROT_ON_A_STICK_BOOST, // 1.11.1 only
|
|
|
|
|
|
|
|
// Rabbit
|
|
|
|
RABBIT_TYPE = _ANIMAL_NEXT,
|
|
|
|
|
|
|
|
// Sheep
|
|
|
|
SHEEP_STATUS = _ANIMAL_NEXT,
|
|
|
|
|
|
|
|
// TameableAnimal
|
|
|
|
TAMEABLE_ANIMAL_STATUS = _ANIMAL_NEXT,
|
|
|
|
TAMEABLE_ANIMAL_OWNER,
|
|
|
|
_TAMEABLE_NEXT,
|
|
|
|
|
|
|
|
// Ocelot
|
|
|
|
OCELOT_TYPE = _TAMEABLE_NEXT,
|
|
|
|
|
|
|
|
// Wolf
|
|
|
|
WOLF_DAMAGE_TAKEN = _TAMEABLE_NEXT,
|
|
|
|
WOLF_BEGGING,
|
|
|
|
WOLF_COLLAR_COLOR,
|
|
|
|
|
|
|
|
// Villager
|
|
|
|
VILLAGER_PROFESSION = _AGEABLE_NEXT,
|
|
|
|
|
|
|
|
// Golem
|
|
|
|
_GOLEM_NEXT = _CREATURE_NEXT,
|
|
|
|
|
|
|
|
// IronGolem
|
|
|
|
IRON_GOLEM_PLAYER_CREATED = _GOLEM_NEXT,
|
|
|
|
|
|
|
|
// Shulker
|
|
|
|
SHULKER_FACING_DIRECTION = _GOLEM_NEXT,
|
|
|
|
SHULKER_ATTACHMENT_FALLING_BLOCK_POSITION,
|
|
|
|
SHULKER_SHIELD_HEIGHT,
|
|
|
|
|
|
|
|
// Monster
|
|
|
|
_MONSTER_NEXT = _CREATURE_NEXT,
|
|
|
|
|
|
|
|
// Blaze
|
|
|
|
BLAZE_ON_FIRE = _MONSTER_NEXT,
|
|
|
|
|
|
|
|
// Creeper
|
|
|
|
CREEPER_STATE = _MONSTER_NEXT,
|
|
|
|
CREEPER_POWERED,
|
|
|
|
CREEPER_IGNITED,
|
|
|
|
|
|
|
|
// Guardian
|
|
|
|
GUARDIAN_STATUS = _MONSTER_NEXT,
|
|
|
|
GUARDIAN_TARGET,
|
|
|
|
|
|
|
|
// Abstract Skeleton
|
|
|
|
ABSTRACT_SKELETON_ARMS_SWINGING = _MONSTER_NEXT,
|
|
|
|
|
|
|
|
// Spider
|
|
|
|
SPIDER_CLIMBING = _MONSTER_NEXT,
|
|
|
|
|
|
|
|
// Witch
|
|
|
|
WITCH_AGGRESIVE = _MONSTER_NEXT,
|
|
|
|
|
|
|
|
// Wither
|
|
|
|
WITHER_FIRST_HEAD_TARGET = _MONSTER_NEXT,
|
|
|
|
WITHER_SECOND_HEAD_TARGET,
|
|
|
|
WITHER_THIRD_HEAD_TARGET,
|
|
|
|
WITHER_INVULNERABLE_TIMER,
|
|
|
|
|
|
|
|
// Zombie
|
|
|
|
ZOMBIE_IS_BABY = _MONSTER_NEXT,
|
|
|
|
ZOMBIE_UNUSED, // Was type
|
|
|
|
ZOMBIE_HANDS_RISED_UP,
|
|
|
|
_ZOMBIE_NEXT,
|
|
|
|
|
|
|
|
// Zombie villager
|
|
|
|
ZOMBIE_VILLAGER_CONVERTING = _ZOMBIE_NEXT,
|
|
|
|
ZOMBIE_VILLAGER_PROFESSION,
|
|
|
|
|
|
|
|
// Enderman
|
|
|
|
ENDERMAN_CARRIED_BLOCK = _MONSTER_NEXT,
|
|
|
|
ENDERMAN_SCREAMING,
|
|
|
|
|
|
|
|
// Evocation illager
|
|
|
|
EVOKER_SPELL = _MONSTER_NEXT,
|
|
|
|
|
|
|
|
// Vex
|
|
|
|
VEX_FLAGS = _MONSTER_NEXT,
|
|
|
|
|
|
|
|
// Vindication illager
|
|
|
|
VINDICATOR_FLAGS = _MONSTER_NEXT,
|
|
|
|
|
|
|
|
// EnderDragon
|
|
|
|
ENDER_DRAGON_DRAGON_PHASE = _INSENTIENT_NEXT,
|
|
|
|
|
|
|
|
// Flying
|
|
|
|
_FLYING_NEXT = _INSENTIENT_NEXT,
|
|
|
|
|
|
|
|
// Ghast
|
|
|
|
GHAST_ATTACKING = _FLYING_NEXT,
|
|
|
|
|
|
|
|
// Slime
|
|
|
|
SLIME_SIZE = _INSENTIENT_NEXT,
|
|
|
|
|
|
|
|
// Minecart
|
|
|
|
MINECART_SHAKING_POWER = _ENTITY_NEXT,
|
|
|
|
MINECART_SHAKING_DIRECTION,
|
|
|
|
MINECART_SHAKING_MULTIPLIER,
|
|
|
|
MINECART_BLOCK_ID_META,
|
|
|
|
MINECART_BLOCK_Y,
|
|
|
|
MINECART_SHOW_BLOCK,
|
|
|
|
_MINECART_NEXT,
|
|
|
|
|
|
|
|
// MinecartCommandBlock
|
|
|
|
MINECART_COMMAND_BLOCK_COMMAND = _MINECART_NEXT,
|
|
|
|
MINECART_COMMAND_BLOCK_LAST_OUTPUT,
|
|
|
|
|
|
|
|
// MinecartFurnace
|
|
|
|
MINECART_FURNACE_POWERED = _MINECART_NEXT,
|
|
|
|
|
|
|
|
// TNTPrimed
|
|
|
|
TNT_PRIMED_FUSE_TIME = _ENTITY_NEXT,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef __clang__
|
|
|
|
#pragma clang diagnostic pop // Restore ignored clang errors
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define HANDLE_READ(ByteBuf, Proc, Type, Var) \
|
|
|
|
Type Var; \
|
|
|
|
if (!ByteBuf.Proc(Var))\
|
|
|
|
{\
|
|
|
|
return;\
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cProtocol_1_12::cProtocol_1_12(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_12::SendSpawnMob(const cMonster & a_Mob)
|
|
|
|
{
|
|
|
|
ASSERT(m_State == 3); // In game mode?
|
|
|
|
|
|
|
|
cPacketizer Pkt(*this, 0x03); // Spawn Mob packet
|
|
|
|
Pkt.WriteVarInt32(a_Mob.GetUniqueID());
|
|
|
|
// TODO: Bad way to write a UUID, and it's not a true UUID, but this is functional for now.
|
|
|
|
Pkt.WriteBEUInt64(0);
|
|
|
|
Pkt.WriteBEUInt64(a_Mob.GetUniqueID());
|
|
|
|
Pkt.WriteVarInt32(static_cast<UInt32>(a_Mob.GetMobType()));
|
|
|
|
Pkt.WriteBEDouble(a_Mob.GetPosX());
|
|
|
|
Pkt.WriteBEDouble(a_Mob.GetPosY());
|
|
|
|
Pkt.WriteBEDouble(a_Mob.GetPosZ());
|
|
|
|
Pkt.WriteByteAngle(a_Mob.GetPitch());
|
|
|
|
Pkt.WriteByteAngle(a_Mob.GetHeadYaw());
|
|
|
|
Pkt.WriteByteAngle(a_Mob.GetYaw());
|
|
|
|
Pkt.WriteBEInt16(static_cast<Int16>(a_Mob.GetSpeedX() * 400));
|
|
|
|
Pkt.WriteBEInt16(static_cast<Int16>(a_Mob.GetSpeedY() * 400));
|
|
|
|
Pkt.WriteBEInt16(static_cast<Int16>(a_Mob.GetSpeedZ() * 400));
|
|
|
|
WriteEntityMetadata(Pkt, a_Mob);
|
|
|
|
Pkt.WriteBEUInt8(0xff); // Metadata terminator
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_12::HandlePacketBlockPlace(cByteBuffer & a_ByteBuffer)
|
|
|
|
{
|
|
|
|
int BlockX, BlockY, BlockZ;
|
|
|
|
if (!a_ByteBuffer.ReadPosition64(BlockX, BlockY, BlockZ))
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
HANDLE_READ(a_ByteBuffer, ReadVarInt, Int32, Face);
|
|
|
|
HANDLE_READ(a_ByteBuffer, ReadVarInt, Int32, Hand);
|
|
|
|
HANDLE_READ(a_ByteBuffer, ReadBEFloat, float, CursorX);
|
|
|
|
HANDLE_READ(a_ByteBuffer, ReadBEFloat, float, CursorY);
|
|
|
|
HANDLE_READ(a_ByteBuffer, ReadBEFloat, float, CursorZ);
|
|
|
|
m_Client->HandleRightClick(BlockX, BlockY, BlockZ, FaceIntToBlockFace(Face), FloorC(CursorX * 16), FloorC(CursorY * 16), FloorC(CursorZ * 16), m_Client->GetPlayer()->GetEquippedItem());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_12::HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer)
|
|
|
|
{
|
|
|
|
cServer * Server = cRoot::Get()->GetServer();
|
|
|
|
AString ServerDescription = Server->GetDescription();
|
|
|
|
int NumPlayers = Server->GetNumPlayers();
|
|
|
|
int MaxPlayers = Server->GetMaxPlayers();
|
|
|
|
AString Favicon = Server->GetFaviconData();
|
|
|
|
cRoot::Get()->GetPluginManager()->CallHookServerPing(*m_Client, ServerDescription, NumPlayers, MaxPlayers, Favicon);
|
|
|
|
|
|
|
|
// Version:
|
|
|
|
Json::Value Version;
|
|
|
|
Version["name"] = "Cuberite 1.12";
|
|
|
|
Version["protocol"] = cProtocolRecognizer::PROTO_VERSION_1_12;
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
if (!Favicon.empty())
|
|
|
|
{
|
|
|
|
ResponseValue["favicon"] = Printf("data:image/png;base64,%s", Favicon.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Serialize the response into a packet:
|
|
|
|
Json::FastWriter Writer;
|
|
|
|
cPacketizer Pkt(*this, 0x00); // Response packet
|
|
|
|
Pkt.WriteString(Writer.write(ResponseValue));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_12::WriteEntityMetadata(cPacketizer & a_Pkt, const cEntity & a_Entity)
|
|
|
|
{
|
|
|
|
using namespace Metadata;
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
a_Pkt.WriteBEUInt8(ENTITY_FLAGS); // Index
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BYTE); // Type
|
|
|
|
a_Pkt.WriteBEInt8(Flags);
|
|
|
|
|
|
|
|
switch (a_Entity.GetEntityType())
|
|
|
|
{
|
|
|
|
case cEntity::etPlayer:
|
|
|
|
{
|
|
|
|
auto & Player = reinterpret_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.
|
|
|
|
a_Pkt.WriteBEUInt8(ENTITY_CUSTOM_NAME);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_STRING);
|
|
|
|
a_Pkt.WriteString(Player.GetName());
|
|
|
|
|
|
|
|
a_Pkt.WriteBEUInt8(LIVING_HEALTH);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_FLOAT);
|
|
|
|
a_Pkt.WriteBEFloat(static_cast<float>(Player.GetHealth()));
|
|
|
|
|
|
|
|
a_Pkt.WriteBEUInt8(PLAYER_DISPLAYED_SKIN_PARTS);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BYTE);
|
|
|
|
a_Pkt.WriteBEUInt8(static_cast<UInt8>(Player.GetSkinParts()));
|
|
|
|
|
|
|
|
a_Pkt.WriteBEUInt8(PLAYER_MAIN_HAND);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BYTE);
|
|
|
|
a_Pkt.WriteBEUInt8(static_cast<UInt8>(Player.GetMainHand()));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case cEntity::etPickup:
|
|
|
|
{
|
|
|
|
a_Pkt.WriteBEUInt8(ITEM_ITEM);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_ITEM);
|
|
|
|
WriteItem(a_Pkt, reinterpret_cast<const cPickup &>(a_Entity).GetItem());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case cEntity::etMinecart:
|
|
|
|
{
|
|
|
|
a_Pkt.WriteBEUInt8(MINECART_SHAKING_POWER);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT);
|
|
|
|
|
|
|
|
// The following expression makes Minecarts shake more with less health or higher damage taken
|
|
|
|
auto & Minecart = reinterpret_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));
|
|
|
|
|
|
|
|
a_Pkt.WriteBEUInt8(MINECART_SHAKING_DIRECTION); // (doesn't seem to effect anything)
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT);
|
|
|
|
a_Pkt.WriteVarInt32(1);
|
|
|
|
|
|
|
|
a_Pkt.WriteBEUInt8(MINECART_SHAKING_MULTIPLIER); // or damage taken
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_FLOAT);
|
|
|
|
a_Pkt.WriteBEFloat(static_cast<float>(Minecart.LastDamage() + 10));
|
|
|
|
|
|
|
|
if (Minecart.GetPayload() == cMinecart::mpNone)
|
|
|
|
{
|
|
|
|
auto & RideableMinecart = reinterpret_cast<const cRideableMinecart &>(Minecart);
|
|
|
|
const cItem & MinecartContent = RideableMinecart.GetContent();
|
|
|
|
if (!MinecartContent.IsEmpty())
|
|
|
|
{
|
|
|
|
a_Pkt.WriteBEUInt8(MINECART_BLOCK_ID_META);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT);
|
|
|
|
int Content = MinecartContent.m_ItemType;
|
|
|
|
Content |= MinecartContent.m_ItemDamage << 8;
|
|
|
|
a_Pkt.WriteVarInt32(static_cast<UInt32>(Content));
|
|
|
|
|
|
|
|
a_Pkt.WriteBEUInt8(MINECART_BLOCK_Y);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT);
|
|
|
|
a_Pkt.WriteVarInt32(static_cast<UInt32>(RideableMinecart.GetBlockHeight()));
|
|
|
|
|
|
|
|
a_Pkt.WriteBEUInt8(MINECART_SHOW_BLOCK);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
|
|
|
|
a_Pkt.WriteBool(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (Minecart.GetPayload() == cMinecart::mpFurnace)
|
|
|
|
{
|
|
|
|
a_Pkt.WriteBEUInt8(MINECART_FURNACE_POWERED);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
|
|
|
|
a_Pkt.WriteBool(reinterpret_cast<const cMinecartWithFurnace &>(Minecart).IsFueled());
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
} // case etMinecart
|
|
|
|
|
|
|
|
case cEntity::etProjectile:
|
|
|
|
{
|
|
|
|
auto & Projectile = reinterpret_cast<const cProjectileEntity &>(a_Entity);
|
|
|
|
switch (Projectile.GetProjectileKind())
|
|
|
|
{
|
|
|
|
case cProjectileEntity::pkArrow:
|
|
|
|
{
|
|
|
|
a_Pkt.WriteBEUInt8(ARROW_CRITICAL);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BYTE);
|
|
|
|
a_Pkt.WriteBEInt8(reinterpret_cast<const cArrowEntity &>(Projectile).IsCritical() ? 1 : 0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case cProjectileEntity::pkFirework:
|
|
|
|
{
|
|
|
|
a_Pkt.WriteBEUInt8(FIREWORK_INFO); // Firework item used for this firework
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_ITEM);
|
|
|
|
WriteItem(a_Pkt, reinterpret_cast<const cFireworkEntity &>(Projectile).GetItem());
|
|
|
|
|
|
|
|
// FIREWORK_BOOSTED_ENTITY_ID, in 1.11.1 only
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case cProjectileEntity::pkSplashPotion:
|
|
|
|
{
|
|
|
|
a_Pkt.WriteBEUInt8(POTION_THROWN); // Potion item which was thrown
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_ITEM);
|
|
|
|
WriteItem(a_Pkt, reinterpret_cast<const cSplashPotionEntity &>(Projectile).GetItem());
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
} // case etProjectile
|
|
|
|
|
|
|
|
case cEntity::etMonster:
|
|
|
|
{
|
|
|
|
WriteMobMetadata(a_Pkt, reinterpret_cast<const cMonster &>(a_Entity));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case cEntity::etBoat:
|
|
|
|
{
|
|
|
|
auto & Boat = reinterpret_cast<const cBoat &>(a_Entity);
|
|
|
|
|
|
|
|
a_Pkt.WriteBEInt8(BOAT_LAST_HIT_TIME);
|
|
|
|
a_Pkt.WriteBEInt8(METADATA_TYPE_VARINT);
|
|
|
|
a_Pkt.WriteVarInt32(static_cast<UInt32>(Boat.GetLastDamage()));
|
|
|
|
|
|
|
|
a_Pkt.WriteBEInt8(BOAT_FORWARD_DIRECTION);
|
|
|
|
a_Pkt.WriteBEInt8(METADATA_TYPE_VARINT);
|
|
|
|
a_Pkt.WriteVarInt32(static_cast<UInt32>(Boat.GetForwardDirection()));
|
|
|
|
|
|
|
|
a_Pkt.WriteBEInt8(BOAT_DAMAGE_TAKEN);
|
|
|
|
a_Pkt.WriteBEInt8(METADATA_TYPE_FLOAT);
|
|
|
|
a_Pkt.WriteBEFloat(Boat.GetDamageTaken());
|
|
|
|
|
|
|
|
a_Pkt.WriteBEInt8(BOAT_TYPE);
|
|
|
|
a_Pkt.WriteBEInt8(METADATA_TYPE_VARINT);
|
|
|
|
a_Pkt.WriteVarInt32(static_cast<UInt32>(Boat.GetMaterial()));
|
|
|
|
|
|
|
|
a_Pkt.WriteBEInt8(BOAT_RIGHT_PADDLE_TURNING);
|
|
|
|
a_Pkt.WriteBEInt8(METADATA_TYPE_BOOL);
|
|
|
|
a_Pkt.WriteBool(Boat.IsRightPaddleUsed());
|
|
|
|
|
|
|
|
a_Pkt.WriteBEInt8(BOAT_LEFT_PADDLE_TURNING);
|
|
|
|
a_Pkt.WriteBEInt8(METADATA_TYPE_BOOL);
|
|
|
|
a_Pkt.WriteBool(Boat.IsLeftPaddleUsed());
|
|
|
|
|
|
|
|
break;
|
|
|
|
} // case etBoat
|
|
|
|
|
|
|
|
case cEntity::etItemFrame:
|
|
|
|
{
|
|
|
|
auto & Frame = reinterpret_cast<const cItemFrame &>(a_Entity);
|
|
|
|
a_Pkt.WriteBEUInt8(ITEM_FRAME_ITEM);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_ITEM);
|
|
|
|
WriteItem(a_Pkt, Frame.GetItem());
|
|
|
|
a_Pkt.WriteBEUInt8(ITEM_FRAME_ROTATION);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT);
|
|
|
|
a_Pkt.WriteVarInt32(Frame.GetItemRotation());
|
|
|
|
break;
|
|
|
|
} // case etItemFrame
|
|
|
|
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_12::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_Mob)
|
|
|
|
{
|
|
|
|
using namespace Metadata;
|
|
|
|
|
|
|
|
// Living Enitiy Metadata
|
|
|
|
if (a_Mob.HasCustomName())
|
|
|
|
{
|
|
|
|
// TODO: As of 1.9 _all_ entities can have custom names; should this be moved up?
|
|
|
|
a_Pkt.WriteBEUInt8(ENTITY_CUSTOM_NAME);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_STRING);
|
|
|
|
a_Pkt.WriteString(a_Mob.GetCustomName());
|
|
|
|
|
|
|
|
a_Pkt.WriteBEUInt8(ENTITY_CUSTOM_NAME_VISIBLE); // Custom name always visible
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
|
|
|
|
a_Pkt.WriteBool(a_Mob.IsCustomNameAlwaysVisible());
|
|
|
|
}
|
|
|
|
|
|
|
|
a_Pkt.WriteBEUInt8(LIVING_HEALTH);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_FLOAT);
|
|
|
|
a_Pkt.WriteBEFloat(static_cast<float>(a_Mob.GetHealth()));
|
|
|
|
|
|
|
|
switch (a_Mob.GetMobType())
|
|
|
|
{
|
|
|
|
case mtBat:
|
|
|
|
{
|
|
|
|
auto & Bat = reinterpret_cast<const cBat &>(a_Mob);
|
|
|
|
a_Pkt.WriteBEUInt8(BAT_HANGING);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BYTE);
|
|
|
|
a_Pkt.WriteBEInt8(Bat.IsHanging() ? 1 : 0);
|
|
|
|
break;
|
|
|
|
} // case mtBat
|
|
|
|
|
|
|
|
case mtCreeper:
|
|
|
|
{
|
|
|
|
auto & Creeper = reinterpret_cast<const cCreeper &>(a_Mob);
|
|
|
|
a_Pkt.WriteBEUInt8(CREEPER_STATE); // (idle or "blowing")
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT);
|
|
|
|
a_Pkt.WriteVarInt32(Creeper.IsBlowing() ? 1 : static_cast<UInt32>(-1));
|
|
|
|
|
|
|
|
a_Pkt.WriteBEUInt8(CREEPER_POWERED);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
|
|
|
|
a_Pkt.WriteBool(Creeper.IsCharged());
|
|
|
|
|
|
|
|
a_Pkt.WriteBEUInt8(CREEPER_IGNITED);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
|
|
|
|
a_Pkt.WriteBool(Creeper.IsBurnedWithFlintAndSteel());
|
|
|
|
break;
|
|
|
|
} // case mtCreeper
|
|
|
|
|
|
|
|
case mtEnderman:
|
|
|
|
{
|
|
|
|
auto & Enderman = reinterpret_cast<const cEnderman &>(a_Mob);
|
|
|
|
a_Pkt.WriteBEUInt8(ENDERMAN_CARRIED_BLOCK);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BLOCKID);
|
|
|
|
UInt32 Carried = 0;
|
|
|
|
Carried |= static_cast<UInt32>(Enderman.GetCarriedBlock() << 4);
|
|
|
|
Carried |= Enderman.GetCarriedMeta();
|
|
|
|
a_Pkt.WriteVarInt32(Carried);
|
|
|
|
|
|
|
|
a_Pkt.WriteBEUInt8(ENDERMAN_SCREAMING);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
|
|
|
|
a_Pkt.WriteBool(Enderman.IsScreaming());
|
|
|
|
break;
|
|
|
|
} // case mtEnderman
|
|
|
|
|
|
|
|
case mtGhast:
|
|
|
|
{
|
|
|
|
auto & Ghast = reinterpret_cast<const cGhast &>(a_Mob);
|
|
|
|
a_Pkt.WriteBEUInt8(GHAST_ATTACKING);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
|
|
|
|
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 = reinterpret_cast<const cHorse &>(a_Mob);
|
|
|
|
Int8 Flags = 0;
|
|
|
|
if (Horse.IsTame())
|
|
|
|
{
|
|
|
|
Flags |= 0x02;
|
|
|
|
}
|
|
|
|
if (Horse.IsSaddled())
|
|
|
|
{
|
|
|
|
Flags |= 0x04;
|
|
|
|
}
|
|
|
|
if (Horse.IsChested())
|
|
|
|
{
|
|
|
|
Flags |= 0x08;
|
|
|
|
}
|
|
|
|
if (Horse.IsEating())
|
|
|
|
{
|
|
|
|
Flags |= 0x20;
|
|
|
|
}
|
|
|
|
if (Horse.IsRearing())
|
|
|
|
{
|
|
|
|
Flags |= 0x40;
|
|
|
|
}
|
|
|
|
if (Horse.IsMthOpen())
|
|
|
|
{
|
|
|
|
Flags |= 0x80;
|
|
|
|
}
|
|
|
|
a_Pkt.WriteBEUInt8(ABSTRACT_HORSE_STATUS);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BYTE);
|
|
|
|
a_Pkt.WriteBEInt8(Flags);
|
|
|
|
|
|
|
|
// This doesn't exist any more; it'll cause horses to all be the normal type
|
|
|
|
// a_Pkt.WriteBEUInt8(HORSE_TYPE);
|
|
|
|
// a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT);
|
|
|
|
// a_Pkt.WriteVarInt32(static_cast<UInt32>(Horse.GetHorseType()));
|
|
|
|
|
|
|
|
// Regular horses
|
|
|
|
a_Pkt.WriteBEUInt8(HORSE_VARIANT); // Color / style
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT);
|
|
|
|
int Appearance = 0;
|
|
|
|
Appearance = Horse.GetHorseColor();
|
|
|
|
Appearance |= Horse.GetHorseStyle() << 8;
|
|
|
|
a_Pkt.WriteVarInt32(static_cast<UInt32>(Appearance));
|
|
|
|
|
|
|
|
a_Pkt.WriteBEUInt8(HORSE_ARMOR);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT);
|
|
|
|
a_Pkt.WriteVarInt32(static_cast<UInt32>(Horse.GetHorseArmour()));
|
|
|
|
|
|
|
|
a_Pkt.WriteBEUInt8(AGEABLE_BABY);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
|
|
|
|
a_Pkt.WriteBool(Horse.IsBaby());
|
|
|
|
break;
|
|
|
|
} // case mtHorse
|
|
|
|
|
|
|
|
case mtMagmaCube:
|
|
|
|
{
|
|
|
|
auto & MagmaCube = reinterpret_cast<const cMagmaCube &>(a_Mob);
|
|
|
|
a_Pkt.WriteBEUInt8(SLIME_SIZE);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT);
|
|
|
|
a_Pkt.WriteVarInt32(static_cast<UInt32>(MagmaCube.GetSize()));
|
|
|
|
break;
|
|
|
|
} // case mtMagmaCube
|
|
|
|
|
|
|
|
case mtOcelot:
|
|
|
|
{
|
|
|
|
auto & Ocelot = reinterpret_cast<const cOcelot &>(a_Mob);
|
|
|
|
|
|
|
|
a_Pkt.WriteBEUInt8(AGEABLE_BABY);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
|
|
|
|
a_Pkt.WriteBool(Ocelot.IsBaby());
|
|
|
|
break;
|
|
|
|
} // case mtOcelot
|
|
|
|
|
|
|
|
case mtCow:
|
|
|
|
{
|
|
|
|
auto & Cow = reinterpret_cast<const cCow &>(a_Mob);
|
|
|
|
|
|
|
|
a_Pkt.WriteBEUInt8(AGEABLE_BABY);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
|
|
|
|
a_Pkt.WriteBool(Cow.IsBaby());
|
|
|
|
break;
|
|
|
|
} // case mtCow
|
|
|
|
|
|
|
|
case mtChicken:
|
|
|
|
{
|
|
|
|
auto & Chicken = reinterpret_cast<const cChicken &>(a_Mob);
|
|
|
|
|
|
|
|
a_Pkt.WriteBEUInt8(AGEABLE_BABY);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
|
|
|
|
a_Pkt.WriteBool(Chicken.IsBaby());
|
|
|
|
break;
|
|
|
|
} // case mtChicken
|
|
|
|
|
|
|
|
case mtPig:
|
|
|
|
{
|
|
|
|
auto & Pig = reinterpret_cast<const cPig &>(a_Mob);
|
|
|
|
|
|
|
|
a_Pkt.WriteBEUInt8(AGEABLE_BABY);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
|
|
|
|
a_Pkt.WriteBool(Pig.IsBaby());
|
|
|
|
|
|
|
|
a_Pkt.WriteBEUInt8(PIG_HAS_SADDLE);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
|
|
|
|
a_Pkt.WriteBool(Pig.IsSaddled());
|
|
|
|
|
|
|
|
// PIG_TOTAL_CARROT_ON_A_STICK_BOOST in 1.11.1 only
|
|
|
|
break;
|
|
|
|
} // case mtPig
|
|
|
|
|
|
|
|
case mtSheep:
|
|
|
|
{
|
|
|
|
auto & Sheep = reinterpret_cast<const cSheep &>(a_Mob);
|
|
|
|
|
|
|
|
a_Pkt.WriteBEUInt8(AGEABLE_BABY);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
|
|
|
|
a_Pkt.WriteBool(Sheep.IsBaby());
|
|
|
|
|
|
|
|
a_Pkt.WriteBEUInt8(SHEEP_STATUS);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BYTE);
|
|
|
|
Int8 SheepMetadata = 0;
|
|
|
|
SheepMetadata = static_cast<Int8>(Sheep.GetFurColor());
|
|
|
|
if (Sheep.IsSheared())
|
|
|
|
{
|
|
|
|
SheepMetadata |= 0x10;
|
|
|
|
}
|
|
|
|
a_Pkt.WriteBEInt8(SheepMetadata);
|
|
|
|
break;
|
|
|
|
} // case mtSheep
|
|
|
|
|
|
|
|
case mtRabbit:
|
|
|
|
{
|
|
|
|
auto & Rabbit = reinterpret_cast<const cRabbit &>(a_Mob);
|
|
|
|
a_Pkt.WriteBEUInt8(AGEABLE_BABY);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
|
|
|
|
a_Pkt.WriteBool(Rabbit.IsBaby());
|
|
|
|
|
|
|
|
a_Pkt.WriteBEUInt8(RABBIT_TYPE);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT);
|
|
|
|
a_Pkt.WriteVarInt32(static_cast<UInt32>(Rabbit.GetRabbitType()));
|
|
|
|
break;
|
|
|
|
} // case mtRabbit
|
|
|
|
|
|
|
|
case mtSkeleton:
|
|
|
|
{
|
|
|
|
// XXX Skeletons are separate entities; all skeletons are currently treated as regular ones
|
|
|
|
|
|
|
|
// auto & Skeleton = reinterpret_cast<const cSkeleton &>(a_Mob);
|
|
|
|
// a_Pkt.WriteBEUInt8(SKELETON_TYPE);
|
|
|
|
// a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT);
|
|
|
|
// a_Pkt.WriteVarInt32(Skeleton.IsWither() ? 1 : 0);
|
|
|
|
break;
|
|
|
|
} // case mtSkeleton
|
|
|
|
|
|
|
|
case mtSlime:
|
|
|
|
{
|
|
|
|
auto & Slime = reinterpret_cast<const cSlime &>(a_Mob);
|
|
|
|
a_Pkt.WriteBEUInt8(SLIME_SIZE);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT);
|
|
|
|
a_Pkt.WriteVarInt32(static_cast<UInt32>(Slime.GetSize()));
|
|
|
|
break;
|
|
|
|
} // case mtSlime
|
|
|
|
|
|
|
|
case mtVillager:
|
|
|
|
{
|
|
|
|
auto & Villager = reinterpret_cast<const cVillager &>(a_Mob);
|
|
|
|
a_Pkt.WriteBEUInt8(AGEABLE_BABY);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
|
|
|
|
a_Pkt.WriteBool(Villager.IsBaby());
|
|
|
|
|
|
|
|
a_Pkt.WriteBEUInt8(VILLAGER_PROFESSION);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT);
|
|
|
|
a_Pkt.WriteVarInt32(static_cast<UInt32>(Villager.GetVilType()));
|
|
|
|
break;
|
|
|
|
} // case mtVillager
|
|
|
|
|
|
|
|
case mtWitch:
|
|
|
|
{
|
|
|
|
auto & Witch = reinterpret_cast<const cWitch &>(a_Mob);
|
|
|
|
a_Pkt.WriteBEUInt8(WITCH_AGGRESIVE);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
|
|
|
|
a_Pkt.WriteBool(Witch.IsAngry());
|
|
|
|
break;
|
|
|
|
} // case mtWitch
|
|
|
|
|
|
|
|
case mtWither:
|
|
|
|
{
|
|
|
|
auto & Wither = reinterpret_cast<const cWither &>(a_Mob);
|
|
|
|
a_Pkt.WriteBEUInt8(WITHER_INVULNERABLE_TIMER);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT);
|
|
|
|
a_Pkt.WriteVarInt32(Wither.GetWitherInvulnerableTicks());
|
|
|
|
|
|
|
|
// TODO: Use boss bar packet for health
|
|
|
|
break;
|
|
|
|
} // case mtWither
|
|
|
|
|
|
|
|
case mtWolf:
|
|
|
|
{
|
|
|
|
auto & Wolf = reinterpret_cast<const cWolf &>(a_Mob);
|
|
|
|
a_Pkt.WriteBEUInt8(AGEABLE_BABY);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
|
|
|
|
a_Pkt.WriteBool(Wolf.IsBaby());
|
|
|
|
|
|
|
|
Int8 WolfStatus = 0;
|
|
|
|
if (Wolf.IsSitting())
|
|
|
|
{
|
|
|
|
WolfStatus |= 0x1;
|
|
|
|
}
|
|
|
|
if (Wolf.IsAngry())
|
|
|
|
{
|
|
|
|
WolfStatus |= 0x2;
|
|
|
|
}
|
|
|
|
if (Wolf.IsTame())
|
|
|
|
{
|
|
|
|
WolfStatus |= 0x4;
|
|
|
|
}
|
|
|
|
a_Pkt.WriteBEUInt8(TAMEABLE_ANIMAL_STATUS);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BYTE);
|
|
|
|
a_Pkt.WriteBEInt8(WolfStatus);
|
|
|
|
|
|
|
|
a_Pkt.WriteBEUInt8(WOLF_DAMAGE_TAKEN);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_FLOAT);
|
|
|
|
a_Pkt.WriteBEFloat(static_cast<float>(a_Mob.GetHealth())); // TODO Not use the current health
|
|
|
|
|
|
|
|
a_Pkt.WriteBEUInt8(WOLF_BEGGING);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
|
|
|
|
a_Pkt.WriteBool(Wolf.IsBegging());
|
|
|
|
|
|
|
|
a_Pkt.WriteBEUInt8(WOLF_COLLAR_COLOR);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_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 = reinterpret_cast<const cZombie &>(a_Mob);
|
|
|
|
a_Pkt.WriteBEUInt8(ZOMBIE_IS_BABY);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
|
|
|
|
a_Pkt.WriteBool(Zombie.IsBaby());
|
|
|
|
|
|
|
|
// These don't exist
|
|
|
|
// a_Pkt.WriteBEUInt8(ZOMBIE_TYPE);
|
|
|
|
// a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT);
|
|
|
|
// a_Pkt.WriteVarInt32(Zombie.IsVillagerZombie() ? 1 : 0);
|
|
|
|
|
|
|
|
// a_Pkt.WriteBEUInt8(ZOMBIE_CONVERTING);
|
|
|
|
// a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
|
|
|
|
// a_Pkt.WriteBool(Zombie.IsConverting());
|
|
|
|
break;
|
|
|
|
} // case mtZombie
|
|
|
|
|
|
|
|
case mtZombiePigman:
|
|
|
|
{
|
|
|
|
auto & ZombiePigman = reinterpret_cast<const cZombiePigman &>(a_Mob);
|
|
|
|
a_Pkt.WriteBEUInt8(AGEABLE_BABY);
|
|
|
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
|
|
|
|
a_Pkt.WriteBool(ZombiePigman.IsBaby());
|
|
|
|
break;
|
|
|
|
} // case mtZombiePigman
|
|
|
|
} // switch (a_Mob.GetType())
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_12::SendEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ)
|
|
|
|
{
|
|
|
|
ASSERT(m_State == 3); // In game mode?
|
|
|
|
|
|
|
|
cPacketizer Pkt(*this, 0x26); // Entity Relative Move packet
|
|
|
|
Pkt.WriteVarInt32(a_Entity.GetUniqueID());
|
|
|
|
// TODO: 1.9 changed these from chars to shorts, meaning that there can be more percision and data. Other code needs to be updated for that.
|
|
|
|
Pkt.WriteBEInt16(a_RelX * 128);
|
|
|
|
Pkt.WriteBEInt16(a_RelY * 128);
|
|
|
|
Pkt.WriteBEInt16(a_RelZ * 128);
|
|
|
|
Pkt.WriteBool(a_Entity.IsOnGround());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_12::SendEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ)
|
|
|
|
{
|
|
|
|
ASSERT(m_State == 3); // In game mode?
|
|
|
|
|
|
|
|
cPacketizer Pkt(*this, 0x27); // Entity Look And Relative Move packet
|
|
|
|
Pkt.WriteVarInt32(a_Entity.GetUniqueID());
|
|
|
|
// TODO: 1.9 changed these from chars to shorts, meaning that there can be more percision and data. Other code needs to be updated for that.
|
|
|
|
Pkt.WriteBEInt16(a_RelX * 128);
|
|
|
|
Pkt.WriteBEInt16(a_RelY * 128);
|
|
|
|
Pkt.WriteBEInt16(a_RelZ * 128);
|
|
|
|
Pkt.WriteByteAngle(a_Entity.GetYaw());
|
|
|
|
Pkt.WriteByteAngle(a_Entity.GetPitch());
|
|
|
|
Pkt.WriteBool(a_Entity.IsOnGround());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_12::SendEntityLook(const cEntity & a_Entity)
|
|
|
|
{
|
|
|
|
ASSERT(m_State == 3); // In game mode?
|
|
|
|
|
|
|
|
cPacketizer Pkt(*this, 0x28); // Entity Look packet
|
|
|
|
Pkt.WriteVarInt32(a_Entity.GetUniqueID());
|
|
|
|
Pkt.WriteByteAngle(a_Entity.GetYaw());
|
|
|
|
Pkt.WriteByteAngle(a_Entity.GetPitch());
|
|
|
|
Pkt.WriteBool(a_Entity.IsOnGround());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_12::SendDestroyEntity(const cEntity & a_Entity)
|
|
|
|
{
|
|
|
|
ASSERT(m_State == 3); // In game mode?
|
|
|
|
|
|
|
|
cPacketizer Pkt(*this, 0x31); // Destroy Entities packet
|
|
|
|
Pkt.WriteVarInt32(1);
|
|
|
|
Pkt.WriteVarInt32(a_Entity.GetUniqueID());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_12::SendRemoveEntityEffect(const cEntity & a_Entity, int a_EffectID)
|
|
|
|
{
|
|
|
|
ASSERT(m_State == 3); // In game mode?
|
|
|
|
|
|
|
|
cPacketizer Pkt(*this, 0x32); // Remove entity effect packet
|
|
|
|
Pkt.WriteVarInt32(a_Entity.GetUniqueID());
|
|
|
|
Pkt.WriteBEUInt8(static_cast<UInt8>(a_EffectID));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_12::SendRespawn(eDimension a_Dimension)
|
|
|
|
{
|
|
|
|
cPacketizer Pkt(*this, 0x34); // Respawn packet
|
|
|
|
cPlayer * Player = m_Client->GetPlayer();
|
|
|
|
Pkt.WriteBEInt32(static_cast<Int32>(a_Dimension));
|
|
|
|
Pkt.WriteBEUInt8(2); // TODO: Difficulty (set to Normal)
|
|
|
|
Pkt.WriteBEUInt8(static_cast<Byte>(Player->GetEffectiveGameMode()));
|
|
|
|
Pkt.WriteString("default");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_12::SendEntityHeadLook(const cEntity & a_Entity)
|
|
|
|
{
|
|
|
|
ASSERT(m_State == 3); // In game mode?
|
|
|
|
|
|
|
|
cPacketizer Pkt(*this, 0x35); // Entity Head Look packet
|
|
|
|
Pkt.WriteVarInt32(a_Entity.GetUniqueID());
|
|
|
|
Pkt.WriteByteAngle(a_Entity.GetHeadYaw());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_12::SendCameraSetTo(const cEntity & a_Entity)
|
|
|
|
{
|
|
|
|
cPacketizer Pkt(*this, 0x38); // Camera Packet (Attach the camera of a player at another entity in spectator mode)
|
|
|
|
Pkt.WriteVarInt32(a_Entity.GetUniqueID());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_12::SendDisplayObjective(const AString & a_Objective, cScoreboard::eDisplaySlot a_Display)
|
|
|
|
{
|
|
|
|
ASSERT(m_State == 3); // In game mode?
|
|
|
|
|
|
|
|
cPacketizer Pkt(*this, 0x3a); // Display scoreboard packet
|
|
|
|
Pkt.WriteBEUInt8(static_cast<UInt8>(a_Display));
|
|
|
|
Pkt.WriteString(a_Objective);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_12::SendEntityMetadata(const cEntity & a_Entity)
|
|
|
|
{
|
|
|
|
ASSERT(m_State == 3); // In game mode?
|
|
|
|
|
|
|
|
cPacketizer Pkt(*this, 0x3b); // Entity Metadata packet
|
|
|
|
Pkt.WriteVarInt32(a_Entity.GetUniqueID());
|
|
|
|
WriteEntityMetadata(Pkt, a_Entity);
|
|
|
|
Pkt.WriteBEUInt8(0xff); // The termination byte
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_12::SendEntityVelocity(const cEntity & a_Entity)
|
|
|
|
{
|
|
|
|
ASSERT(m_State == 3); // In game mode?
|
|
|
|
|
|
|
|
cPacketizer Pkt(*this, 0x3d); // Entity Velocity packet
|
|
|
|
Pkt.WriteVarInt32(a_Entity.GetUniqueID());
|
|
|
|
// 400 = 8000 / 20 ... Conversion from our speed in m / s to 8000 m / tick
|
|
|
|
Pkt.WriteBEInt16(static_cast<Int16>(a_Entity.GetSpeedX() * 400));
|
|
|
|
Pkt.WriteBEInt16(static_cast<Int16>(a_Entity.GetSpeedY() * 400));
|
|
|
|
Pkt.WriteBEInt16(static_cast<Int16>(a_Entity.GetSpeedZ() * 400));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_12::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item)
|
|
|
|
{
|
|
|
|
ASSERT(m_State == 3); // In game mode?
|
|
|
|
|
|
|
|
cPacketizer Pkt(*this, 0x3e); // Entity Equipment packet
|
|
|
|
Pkt.WriteVarInt32(a_Entity.GetUniqueID());
|
|
|
|
// Needs to be adjusted due to the insertion of offhand at slot 1
|
|
|
|
if (a_SlotNum > 0)
|
|
|
|
{
|
|
|
|
a_SlotNum++;
|
|
|
|
}
|
|
|
|
Pkt.WriteVarInt32(static_cast<UInt32>(a_SlotNum));
|
|
|
|
WriteItem(Pkt, a_Item);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_12::SendExperience(void)
|
|
|
|
{
|
|
|
|
ASSERT(m_State == 3); // In game mode?
|
|
|
|
|
|
|
|
cPacketizer Pkt(*this, 0x3f); // Experience Packet
|
|
|
|
cPlayer * Player = m_Client->GetPlayer();
|
|
|
|
Pkt.WriteBEFloat(Player->GetXpPercentage());
|
|
|
|
Pkt.WriteVarInt32(static_cast<UInt32>(Player->GetXpLevel()));
|
|
|
|
Pkt.WriteVarInt32(static_cast<UInt32>(Player->GetCurrentXp()));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_12::SendHealth(void)
|
|
|
|
{
|
|
|
|
ASSERT(m_State == 3); // In game mode?
|
|
|
|
|
|
|
|
cPacketizer Pkt(*this, 0x40); // Update Health packet
|
|
|
|
cPlayer * Player = m_Client->GetPlayer();
|
|
|
|
Pkt.WriteBEFloat(static_cast<float>(Player->GetHealth()));
|
|
|
|
Pkt.WriteVarInt32(static_cast<UInt32>(Player->GetFoodLevel()));
|
|
|
|
Pkt.WriteBEFloat(static_cast<float>(Player->GetFoodSaturationLevel()));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_12::SendScoreboardObjective(const AString & a_Name, const AString & a_DisplayName, Byte a_Mode)
|
|
|
|
{
|
|
|
|
ASSERT(m_State == 3); // In game mode?
|
|
|
|
|
|
|
|
cPacketizer Pkt(*this, 0x41); // Scoreboard objective packet
|
|
|
|
Pkt.WriteString(a_Name);
|
|
|
|
Pkt.WriteBEUInt8(a_Mode);
|
|
|
|
if ((a_Mode == 0) || (a_Mode == 2))
|
|
|
|
{
|
|
|
|
Pkt.WriteString(a_DisplayName);
|
|
|
|
Pkt.WriteString("integer");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_12::SendAttachEntity(const cEntity & a_Entity, const cEntity & a_Vehicle)
|
|
|
|
{
|
|
|
|
ASSERT(m_State == 3); // In game mode?
|
|
|
|
cPacketizer Pkt(*this, 0x42); // Set passangers packet
|
|
|
|
Pkt.WriteVarInt32(a_Vehicle.GetUniqueID());
|
|
|
|
Pkt.WriteVarInt32(1); // 1 passenger
|
|
|
|
Pkt.WriteVarInt32(a_Entity.GetUniqueID());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_12::SendDetachEntity(const cEntity & a_Entity, const cEntity & a_PreviousVehicle)
|
|
|
|
{
|
|
|
|
ASSERT(m_State == 3); // In game mode?
|
|
|
|
cPacketizer Pkt(*this, 0x42); // Set passangers packet
|
|
|
|
Pkt.WriteVarInt32(a_PreviousVehicle.GetUniqueID());
|
|
|
|
Pkt.WriteVarInt32(0); // No passangers
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_12::SendScoreUpdate(const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode)
|
|
|
|
{
|
|
|
|
ASSERT(m_State == 3); // In game mode?
|
|
|
|
|
|
|
|
cPacketizer Pkt(*this, 0x44); // Update score packet
|
|
|
|
Pkt.WriteString(a_Player);
|
|
|
|
Pkt.WriteBEUInt8(a_Mode);
|
|
|
|
Pkt.WriteString(a_Objective);
|
|
|
|
|
|
|
|
if (a_Mode != 1)
|
|
|
|
{
|
|
|
|
Pkt.WriteVarInt32(static_cast<UInt32>(a_Score));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_12::SendLogin(const cPlayer & a_Player, const cWorld & a_World)
|
|
|
|
{
|
|
|
|
// Send the Join Game packet:
|
|
|
|
{
|
|
|
|
cServer * Server = cRoot::Get()->GetServer();
|
|
|
|
cPacketizer Pkt(*this, 0x23); // Join Game packet
|
|
|
|
Pkt.WriteBEUInt32(a_Player.GetUniqueID());
|
|
|
|
Pkt.WriteBEUInt8(static_cast<UInt8>(a_Player.GetEffectiveGameMode()) | (Server->IsHardcore() ? 0x08 : 0)); // Hardcore flag bit 4
|
|
|
|
Pkt.WriteBEInt32(static_cast<Int32>(a_World.GetDimension()));
|
|
|
|
Pkt.WriteBEUInt8(2); // TODO: Difficulty (set to Normal)
|
|
|
|
Pkt.WriteBEUInt8(static_cast<UInt8>(Clamp<int>(Server->GetMaxPlayers(), 0, 255)));
|
|
|
|
Pkt.WriteString("default"); // Level type - wtf?
|
|
|
|
Pkt.WriteBool(false); // Reduced Debug Info - wtf?
|
|
|
|
}
|
|
|
|
|
|
|
|
// Send the spawn position:
|
|
|
|
{
|
|
|
|
cPacketizer Pkt(*this, 0x45); // Spawn Position packet
|
|
|
|
Pkt.WritePosition64(FloorC(a_World.GetSpawnX()), FloorC(a_World.GetSpawnY()), FloorC(a_World.GetSpawnZ()));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Send the server difficulty:
|
|
|
|
{
|
|
|
|
cPacketizer Pkt(*this, 0x0d); // Server difficulty packet
|
|
|
|
Pkt.WriteBEInt8(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Send player abilities:
|
|
|
|
SendPlayerAbilities();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-07-07 03:31:45 -04:00
|
|
|
void cProtocol_1_12::SendUpdateBlockEntity(cBlockEntity & a_BlockEntity)
|
|
|
|
{
|
|
|
|
ASSERT(m_State == 3); // In game mode?
|
|
|
|
|
|
|
|
cPacketizer Pkt(*this, 0x09); // Update tile entity packet
|
|
|
|
Pkt.WritePosition64(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_FLOWER_POT: Action = 5; break; // Update flower pot
|
|
|
|
case E_BLOCK_BED: Action = 11; break; // Update bed color
|
|
|
|
default: ASSERT(!"Unhandled or unimplemented BlockEntity update request!"); break;
|
|
|
|
}
|
|
|
|
Pkt.WriteBEUInt8(Action);
|
|
|
|
|
|
|
|
WriteBlockEntity(Pkt, a_BlockEntity);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-06-14 05:22:51 -04:00
|
|
|
void cProtocol_1_12::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay, bool a_DoDaylightCycle)
|
|
|
|
{
|
|
|
|
ASSERT(m_State == 3); // In game mode?
|
|
|
|
if (!a_DoDaylightCycle)
|
|
|
|
{
|
|
|
|
// When writing a "-" before the number the client ignores it but it will stop the client-side time expiration.
|
|
|
|
a_TimeOfDay = std::min(-a_TimeOfDay, -1LL);
|
|
|
|
}
|
|
|
|
|
|
|
|
cPacketizer Pkt(*this, 0x46); // Time update packet
|
|
|
|
Pkt.WriteBEInt64(a_WorldAge);
|
|
|
|
Pkt.WriteBEInt64(a_TimeOfDay);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-06-14 15:01:09 -04:00
|
|
|
void cProtocol_1_12::SendSetRawTitle(const AString & a_Title)
|
|
|
|
{
|
|
|
|
ASSERT(m_State == 3); // In game mode?
|
|
|
|
|
|
|
|
cPacketizer Pkt(*this, 0x47); // Title packet
|
|
|
|
Pkt.WriteVarInt32(0); // Set title
|
|
|
|
|
|
|
|
Pkt.WriteString(a_Title);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_12::SendSetRawSubTitle(const AString & a_SubTitle)
|
|
|
|
{
|
|
|
|
ASSERT(m_State == 3); // In game mode?
|
|
|
|
|
|
|
|
cPacketizer Pkt(*this, 0x47); // Title packet
|
|
|
|
Pkt.WriteVarInt32(1); // Set subtitle
|
|
|
|
|
|
|
|
Pkt.WriteString(a_SubTitle);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-06-14 05:22:51 -04:00
|
|
|
void cProtocol_1_12::SendTitleTimes(int a_FadeInTicks, int a_DisplayTicks, int a_FadeOutTicks)
|
|
|
|
{
|
|
|
|
ASSERT(m_State == 3); // In game mode?
|
|
|
|
|
|
|
|
cPacketizer Pkt(*this, 0x47); // Title packet
|
|
|
|
Pkt.WriteVarInt32(3); // Set title display times
|
|
|
|
Pkt.WriteBEInt32(a_FadeInTicks);
|
|
|
|
Pkt.WriteBEInt32(a_DisplayTicks);
|
|
|
|
Pkt.WriteBEInt32(a_FadeOutTicks);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_12::SendHideTitle(void)
|
|
|
|
{
|
|
|
|
ASSERT(m_State == 3); // In game mode?
|
|
|
|
|
|
|
|
cPacketizer Pkt(*this, 0x47); // Title packet
|
|
|
|
Pkt.WriteVarInt32(4); // Hide title
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_12::SendResetTitle(void)
|
|
|
|
{
|
|
|
|
ASSERT(m_State == 3); // In game mode?
|
|
|
|
|
|
|
|
cPacketizer Pkt(*this, 0x47); // Title packet
|
|
|
|
Pkt.WriteVarInt32(5); // Reset title
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_12::SendCollectEntity(const cEntity & a_Entity, const cPlayer & a_Player, int a_Count)
|
|
|
|
{
|
|
|
|
ASSERT(m_State == 3); // In game mode?
|
|
|
|
|
|
|
|
cPacketizer Pkt(*this, 0x4a); // Collect Item packet
|
|
|
|
Pkt.WriteVarInt32(a_Entity.GetUniqueID());
|
|
|
|
Pkt.WriteVarInt32(a_Player.GetUniqueID());
|
|
|
|
Pkt.WriteVarInt32(static_cast<UInt32>(a_Count));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_12::SendTeleportEntity(const cEntity & a_Entity)
|
|
|
|
{
|
|
|
|
ASSERT(m_State == 3); // In game mode?
|
|
|
|
|
|
|
|
cPacketizer Pkt(*this, 0x4b); // Entity teleport packet
|
|
|
|
Pkt.WriteVarInt32(a_Entity.GetUniqueID());
|
|
|
|
Pkt.WriteBEDouble(a_Entity.GetPosX());
|
|
|
|
Pkt.WriteBEDouble(a_Entity.GetPosY());
|
|
|
|
Pkt.WriteBEDouble(a_Entity.GetPosZ());
|
|
|
|
Pkt.WriteByteAngle(a_Entity.GetYaw());
|
|
|
|
Pkt.WriteByteAngle(a_Entity.GetPitch());
|
|
|
|
Pkt.WriteBool(a_Entity.IsOnGround());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_12::SendEntityProperties(const cEntity & a_Entity)
|
|
|
|
{
|
|
|
|
ASSERT(m_State == 3); // In game mode?
|
|
|
|
|
|
|
|
|
|
|
|
cPacketizer Pkt(*this, 0x4d); // Entity Properties packet
|
|
|
|
Pkt.WriteVarInt32(a_Entity.GetUniqueID());
|
|
|
|
WriteEntityProperties(Pkt, a_Entity);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_12::SendPlayerMaxSpeed(void)
|
|
|
|
{
|
|
|
|
ASSERT(m_State == 3); // In game mode?
|
|
|
|
|
|
|
|
cPacketizer Pkt(*this, 0x4d); // Entity Properties
|
|
|
|
cPlayer * Player = m_Client->GetPlayer();
|
|
|
|
Pkt.WriteVarInt32(Player->GetUniqueID());
|
|
|
|
Pkt.WriteBEInt32(1); // Count
|
|
|
|
Pkt.WriteString("generic.movementSpeed");
|
|
|
|
// The default game speed is 0.1, multiply that value by the relative speed:
|
|
|
|
Pkt.WriteBEDouble(0.1 * Player->GetNormalMaxSpeed());
|
|
|
|
if (Player->IsSprinting())
|
|
|
|
{
|
|
|
|
Pkt.WriteVarInt32(1); // Modifier count
|
|
|
|
Pkt.WriteBEUInt64(0x662a6b8dda3e4c1c);
|
|
|
|
Pkt.WriteBEUInt64(0x881396ea6097278d); // UUID of the modifier
|
|
|
|
Pkt.WriteBEDouble(Player->GetSprintingMaxSpeed() - Player->GetNormalMaxSpeed());
|
|
|
|
Pkt.WriteBEUInt8(2);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Pkt.WriteVarInt32(0); // Modifier count
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cProtocol_1_12::SendEntityEffect(const cEntity & a_Entity, int a_EffectID, int a_Amplifier, short a_Duration)
|
|
|
|
{
|
|
|
|
ASSERT(m_State == 3); // In game mode?
|
|
|
|
|
|
|
|
cPacketizer Pkt(*this, 0x4e); // Entity Effect packet
|
|
|
|
Pkt.WriteVarInt32(a_Entity.GetUniqueID());
|
|
|
|
Pkt.WriteBEUInt8(static_cast<UInt8>(a_EffectID));
|
|
|
|
Pkt.WriteBEUInt8(static_cast<UInt8>(a_Amplifier));
|
|
|
|
Pkt.WriteVarInt32(static_cast<UInt32>(a_Duration));
|
|
|
|
Pkt.WriteBool(false); // Hide particles
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool cProtocol_1_12::HandlePacket(cByteBuffer & a_ByteBuffer, UInt32 a_PacketType)
|
|
|
|
{
|
|
|
|
switch (m_State)
|
|
|
|
{
|
|
|
|
case 1:
|
|
|
|
{
|
|
|
|
// Status
|
|
|
|
switch (a_PacketType)
|
|
|
|
{
|
|
|
|
case 0x00: HandlePacketStatusRequest(a_ByteBuffer); return true;
|
|
|
|
case 0x01: HandlePacketStatusPing(a_ByteBuffer); return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
{
|
|
|
|
// Login
|
|
|
|
switch (a_PacketType)
|
|
|
|
{
|
|
|
|
case 0x00: HandlePacketLoginStart(a_ByteBuffer); return true;
|
|
|
|
case 0x01: HandlePacketLoginEncryptionResponse(a_ByteBuffer); return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 3:
|
|
|
|
{
|
|
|
|
// Game
|
|
|
|
switch (a_PacketType)
|
|
|
|
{
|
|
|
|
case 0x00: HandleConfirmTeleport(a_ByteBuffer); return true;
|
|
|
|
case 0x01: break; // Prepare Crafting Grid, not yet implemented
|
|
|
|
case 0x02: HandlePacketTabComplete(a_ByteBuffer); return true;
|
|
|
|
case 0x03: HandlePacketChatMessage(a_ByteBuffer); return true;
|
|
|
|
case 0x04: HandlePacketClientStatus(a_ByteBuffer); return true;
|
|
|
|
case 0x05: 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 0x0b: HandlePacketUseEntity(a_ByteBuffer); return true;
|
|
|
|
case 0x0c: HandlePacketKeepAlive(a_ByteBuffer); return true;
|
|
|
|
case 0x0d: HandlePacketPlayer(a_ByteBuffer); return true;
|
|
|
|
case 0x0e: HandlePacketPlayerPos(a_ByteBuffer); return true;
|
|
|
|
case 0x0f: HandlePacketPlayerPosLook(a_ByteBuffer); return true;
|
|
|
|
case 0x10: HandlePacketPlayerLook(a_ByteBuffer); return true;
|
|
|
|
case 0x11: HandlePacketVehicleMove(a_ByteBuffer); return true;
|
|
|
|
case 0x12: HandlePacketBoatSteer(a_ByteBuffer); return true;
|
|
|
|
case 0x13: HandlePacketPlayerAbilities(a_ByteBuffer); return true;
|
|
|
|
case 0x14: HandlePacketBlockDig(a_ByteBuffer); return true;
|
|
|
|
case 0x15: HandlePacketEntityAction(a_ByteBuffer); return true;
|
|
|
|
case 0x16: HandlePacketSteerVehicle(a_ByteBuffer); return true;
|
|
|
|
case 0x17: break; // Crafting Book Data - not yet implemented
|
|
|
|
case 0x18: break; // Resource pack status - not yet implemented
|
|
|
|
case 0x19: break; // Advancement Tab - not yet implemented
|
|
|
|
case 0x1a: HandlePacketSlotSelect(a_ByteBuffer); return true;
|
|
|
|
case 0x1b: HandlePacketCreativeInventoryAction(a_ByteBuffer); return true;
|
|
|
|
case 0x1c: HandlePacketUpdateSign(a_ByteBuffer); return true;
|
|
|
|
case 0x1d: HandlePacketAnimation(a_ByteBuffer); return true;
|
|
|
|
case 0x1e: HandlePacketSpectate(a_ByteBuffer); return true;
|
|
|
|
case 0x1f: HandlePacketBlockPlace(a_ByteBuffer); return true;
|
|
|
|
case 0x20: HandlePacketUseItem(a_ByteBuffer); return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
// Received a packet in an unknown state, report:
|
|
|
|
LOGWARNING("Received a packet in an unknown protocol state %d. Ignoring further packets.", m_State);
|
|
|
|
|
|
|
|
// Cannot kick the client - we don't know this state and thus the packet number for the kick packet
|
|
|
|
|
|
|
|
// Switch to a state when all further packets are silently ignored:
|
|
|
|
m_State = 255;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
case 255:
|
|
|
|
{
|
|
|
|
// This is the state used for "not processing packets anymore" when we receive a bad packet from a client.
|
|
|
|
// Do not output anything (the caller will do that for us), just return failure
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} // switch (m_State)
|
|
|
|
|
|
|
|
// Unknown packet type, report to the ClientHandle:
|
|
|
|
m_Client->PacketUnknown(a_PacketType);
|
|
|
|
return false;
|
|
|
|
}
|