// 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(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(a_Mob.GetSpeedX() * 400)); Pkt.WriteBEInt16(static_cast(a_Mob.GetSpeedY() * 400)); Pkt.WriteBEInt16(static_cast(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(); auto NumPlayers = static_cast(Server->GetNumPlayers()); auto MaxPlayers = static_cast(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(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(Player.GetHealth())); a_Pkt.WriteBEUInt8(PLAYER_DISPLAYED_SKIN_PARTS); a_Pkt.WriteBEUInt8(METADATA_TYPE_BYTE); a_Pkt.WriteBEUInt8(static_cast(Player.GetSkinParts())); a_Pkt.WriteBEUInt8(PLAYER_MAIN_HAND); a_Pkt.WriteBEUInt8(METADATA_TYPE_BYTE); a_Pkt.WriteBEUInt8(static_cast(Player.GetMainHand())); break; } case cEntity::etPickup: { a_Pkt.WriteBEUInt8(ITEM_ITEM); a_Pkt.WriteBEUInt8(METADATA_TYPE_ITEM); WriteItem(a_Pkt, reinterpret_cast(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(a_Entity); auto maxHealth = a_Entity.GetMaxHealth(); auto curHealth = a_Entity.GetHealth(); a_Pkt.WriteVarInt32(static_cast((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(Minecart.LastDamage() + 10)); if (Minecart.GetPayload() == cMinecart::mpNone) { auto & RideableMinecart = reinterpret_cast(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(Content)); a_Pkt.WriteBEUInt8(MINECART_BLOCK_Y); a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT); a_Pkt.WriteVarInt32(static_cast(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(Minecart).IsFueled()); } break; } // case etMinecart case cEntity::etProjectile: { auto & Projectile = reinterpret_cast(a_Entity); switch (Projectile.GetProjectileKind()) { case cProjectileEntity::pkArrow: { a_Pkt.WriteBEUInt8(ARROW_CRITICAL); a_Pkt.WriteBEUInt8(METADATA_TYPE_BYTE); a_Pkt.WriteBEInt8(reinterpret_cast(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(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(Projectile).GetItem()); } default: { break; } } break; } // case etProjectile case cEntity::etMonster: { WriteMobMetadata(a_Pkt, reinterpret_cast(a_Entity)); break; } case cEntity::etBoat: { auto & Boat = reinterpret_cast(a_Entity); a_Pkt.WriteBEInt8(BOAT_LAST_HIT_TIME); a_Pkt.WriteBEInt8(METADATA_TYPE_VARINT); a_Pkt.WriteVarInt32(static_cast(Boat.GetLastDamage())); a_Pkt.WriteBEInt8(BOAT_FORWARD_DIRECTION); a_Pkt.WriteBEInt8(METADATA_TYPE_VARINT); a_Pkt.WriteVarInt32(static_cast(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(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(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(a_Mob.GetHealth())); switch (a_Mob.GetMobType()) { case mtBat: { auto & Bat = reinterpret_cast(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(a_Mob); a_Pkt.WriteBEUInt8(CREEPER_STATE); // (idle or "blowing") a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT); a_Pkt.WriteVarInt32(Creeper.IsBlowing() ? 1 : static_cast(-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(a_Mob); a_Pkt.WriteBEUInt8(ENDERMAN_CARRIED_BLOCK); a_Pkt.WriteBEUInt8(METADATA_TYPE_BLOCKID); UInt32 Carried = 0; Carried |= static_cast(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(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(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(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(Appearance)); a_Pkt.WriteBEUInt8(HORSE_ARMOR); a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT); a_Pkt.WriteVarInt32(static_cast(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(a_Mob); a_Pkt.WriteBEUInt8(SLIME_SIZE); a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT); a_Pkt.WriteVarInt32(static_cast(MagmaCube.GetSize())); break; } // case mtMagmaCube case mtOcelot: { auto & Ocelot = reinterpret_cast(a_Mob); a_Pkt.WriteBEUInt8(AGEABLE_BABY); a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL); a_Pkt.WriteBool(Ocelot.IsBaby()); Int8 OcelotStatus = 0; if (Ocelot.IsSitting()) { OcelotStatus |= 0x1; } if (Ocelot.IsTame()) { OcelotStatus |= 0x4; } a_Pkt.WriteBEUInt8(TAMEABLE_ANIMAL_STATUS); a_Pkt.WriteBEUInt8(METADATA_TYPE_BYTE); a_Pkt.WriteBEInt8(OcelotStatus); a_Pkt.WriteBEUInt8(OCELOT_TYPE); a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT); a_Pkt.WriteVarInt32(static_cast(Ocelot.GetOcelotType())); break; } // case mtOcelot case mtCow: { auto & Cow = reinterpret_cast(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(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(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(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(Sheep.GetFurColor()); if (Sheep.IsSheared()) { SheepMetadata |= 0x10; } a_Pkt.WriteBEInt8(SheepMetadata); break; } // case mtSheep case mtRabbit: { auto & Rabbit = reinterpret_cast(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(Rabbit.GetRabbitType())); break; } // case mtRabbit case mtSkeleton: { // XXX Skeletons are separate entities; all skeletons are currently treated as regular ones // auto & Skeleton = reinterpret_cast(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(a_Mob); a_Pkt.WriteBEUInt8(SLIME_SIZE); a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT); a_Pkt.WriteVarInt32(static_cast(Slime.GetSize())); break; } // case mtSlime case mtVillager: { auto & Villager = reinterpret_cast(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(Villager.GetVilType())); break; } // case mtVillager case mtWitch: { auto & Witch = reinterpret_cast(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(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(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(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(Wolf.GetCollarColor())); break; } // case mtWolf case mtZombie: { // XXX Zombies were also split into new sublcasses; this doesn't handle that. auto & Zombie = reinterpret_cast(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(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(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(a_Dimension)); Pkt.WriteBEUInt8(2); // TODO: Difficulty (set to Normal) Pkt.WriteBEUInt8(static_cast(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(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(a_Entity.GetSpeedX() * 400)); Pkt.WriteBEInt16(static_cast(a_Entity.GetSpeedY() * 400)); Pkt.WriteBEInt16(static_cast(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(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(Player->GetXpLevel())); Pkt.WriteVarInt32(static_cast(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(Player->GetHealth())); Pkt.WriteVarInt32(static_cast(Player->GetFoodLevel())); Pkt.WriteBEFloat(static_cast(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(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(a_Player.GetEffectiveGameMode()) | (Server->IsHardcore() ? 0x08 : 0)); // Hardcore flag bit 4 Pkt.WriteBEInt32(static_cast(a_World.GetDimension())); Pkt.WriteBEUInt8(2); // TODO: Difficulty (set to Normal) Pkt.WriteBEUInt8(static_cast(Clamp(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(); } 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); } void cProtocol_1_12::HandlePacketCraftingBookData(cByteBuffer & a_ByteBuffer) { a_ByteBuffer.SkipRead(a_ByteBuffer.GetReadableSpace() - 1); m_Client->GetPlayer()->SendMessageInfo("The green crafting book feature is not implemented yet."); } void cProtocol_1_12::HandlePacketAdvancementTab(cByteBuffer & a_ByteBuffer) { a_ByteBuffer.SkipRead(a_ByteBuffer.GetReadableSpace() - 1); m_Client->GetPlayer()->SendMessageInfo("The new advancements are not implemented."); } 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); } 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); } 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(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(a_EffectID)); Pkt.WriteBEUInt8(static_cast(a_Amplifier)); Pkt.WriteVarInt32(static_cast(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: HandlePacketCraftingBookData(a_ByteBuffer); return true; case 0x18: break; // Resource pack status - not yet implemented case 0x19: HandlePacketAdvancementTab(a_ByteBuffer); return true; 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; }