From 61078e8402dc289cca0ace12933a688660d10b03 Mon Sep 17 00:00:00 2001 From: bibo38 Date: Fri, 2 Sep 2016 19:22:06 +0200 Subject: [PATCH] Added support for the Minecraft 1.10 protocol(#210) (#3348) * Added support for the Minecraft 1.10 protocol(#210) * Fixed the Clang compilation errors * Fixed wrong sound pitch value and fixed SendPlayerSpawn Metadata value. * Prefixed each enum item with the appropriate class name. --- CONTRIBUTORS | 1 + src/Protocol/CMakeLists.txt | 7 +- src/Protocol/Protocol110x.cpp | 879 ++++++++++++++++++++++++++++ src/Protocol/Protocol110x.h | 34 ++ src/Protocol/Protocol19x.cpp | 40 +- src/Protocol/Protocol19x.h | 4 +- src/Protocol/ProtocolRecognizer.cpp | 17 +- src/Protocol/ProtocolRecognizer.h | 15 +- 8 files changed, 960 insertions(+), 37 deletions(-) create mode 100644 src/Protocol/Protocol110x.cpp create mode 100644 src/Protocol/Protocol110x.h diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 5cea4df24..53df4fc03 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -3,6 +3,7 @@ Many people have contributed to Cuberite, and this list attempts to broadcast at BasedDoge (Donated AlchemistVillage prefabs) bearbin (Alexander Harkness) beeduck +bibo38 birkett (Anthony Birkett) derouinw Diusrex diff --git a/src/Protocol/CMakeLists.txt b/src/Protocol/CMakeLists.txt index f3282c93f..13afb76f4 100644 --- a/src/Protocol/CMakeLists.txt +++ b/src/Protocol/CMakeLists.txt @@ -9,6 +9,7 @@ SET (SRCS Packetizer.cpp Protocol18x.cpp Protocol19x.cpp + Protocol110x.cpp ProtocolRecognizer.cpp ) @@ -20,12 +21,14 @@ SET (HDRS Protocol.h Protocol18x.h Protocol19x.h + Protocol110x.h ProtocolRecognizer.h ) if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - set_source_files_properties(Protocol19x.cpp PROPERTIES COMPILE_FLAGS "-Wno-error=switch-enum -Wno-error=switch") - set_source_files_properties(Protocol18x.cpp PROPERTIES COMPILE_FLAGS "-Wno-error=switch-enum -Wno-error=switch") + set_source_files_properties(Protocol19x.cpp PROPERTIES COMPILE_FLAGS "-Wno-error=switch-enum -Wno-error=switch") + set_source_files_properties(Protocol18x.cpp PROPERTIES COMPILE_FLAGS "-Wno-error=switch-enum -Wno-error=switch") + set_source_files_properties(Protocol110x.cpp PROPERTIES COMPILE_FLAGS "-Wno-error=switch") endif() if (NOT MSVC) diff --git a/src/Protocol/Protocol110x.cpp b/src/Protocol/Protocol110x.cpp new file mode 100644 index 000000000..a117d8750 --- /dev/null +++ b/src/Protocol/Protocol110x.cpp @@ -0,0 +1,879 @@ + +// Protocol110x.cpp + +/* +Implements the 1.10.x protocol classes: + - cProtocol1100 + - release 1.10.0 protocol (#210) +(others may be added later in the future for the 1.10 release series) +*/ + +#include "Globals.h" +#include "Protocol110x.h" +#include "Packetizer.h" + +#include "../Root.h" +#include "../Server.h" + +#include "../Entities/Boat.h" +#include "../Entities/ExpOrb.h" +#include "../Entities/Minecart.h" +#include "../Entities/FallingBlock.h" +#include "../Entities/Painting.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 "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! +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wduplicate-enum" + +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, + + // 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, + + // Horse + HORSE_STATUS = _ANIMAL_NEXT, + HORSE_TYPE, + HORSE_VARIANT, + HORSE_OWNER, + HORSE_ARMOR, + + // Pig + PIG_HAS_SADDLE = _ANIMAL_NEXT, + + // 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, + + // Skeleton + SKELETON_TYPE = _MONSTER_NEXT, + SKELETON_ARMS_SWINGING, + + // 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_TYPE, + ZOMBIE_CONVERTING, + ZOMBIE_HANDS_RISED_UP, + + // Enderman + ENDERMAN_CARRIED_BLOCK = _MONSTER_NEXT, + ENDERMAN_SCREAMING, + + // 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, + }; +} + +#pragma clang diagnostic pop // Restore ignored clang errors + + + + + +cProtocol1100::cProtocol1100(cClientHandle * a_Client, const AString &a_ServerAddress, UInt16 a_ServerPort, UInt32 a_State) : + super(a_Client, a_ServerAddress, a_ServerPort, a_State) +{ +} + + + + + +void cProtocol1100::SendSoundEffect(const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) +{ + ASSERT(m_State == 3); // In game mode? + + cPacketizer Pkt(*this, 0x19); // Named sound effect packet + Pkt.WriteString(a_SoundName); + Pkt.WriteVarInt32(0); // Master sound category (may want to be changed to a parameter later) + Pkt.WriteBEInt32(FloorC(a_X * 8.0)); + Pkt.WriteBEInt32(FloorC(a_Y * 8.0)); + Pkt.WriteBEInt32(FloorC(a_Z * 8.0)); + Pkt.WriteBEFloat(a_Volume); + Pkt.WriteBEFloat(a_Pitch); +} + + + + + +void cProtocol1100::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.10"; + Version["protocol"] = 210; + + // 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()); + } + + Json::StyledWriter Writer; + AString Response = Writer.write(ResponseValue); + + cPacketizer Pkt(*this, 0x00); // Response packet + Pkt.WriteString(Response); +} + + + + + +void cProtocol1100::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())); + 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()); + 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.WriteBEInt32(Boat.GetLastDamage()); + + a_Pkt.WriteBEInt8(BOAT_FORWARD_DIRECTION); + a_Pkt.WriteBEInt8(METADATA_TYPE_VARINT); + a_Pkt.WriteBEInt32(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.WriteBEInt32(Boat.GetType()); + + 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 cProtocol1100::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: + { + 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(HORSE_STATUS); + a_Pkt.WriteBEUInt8(METADATA_TYPE_BYTE); + a_Pkt.WriteBEInt8(Flags); + + a_Pkt.WriteBEUInt8(HORSE_TYPE); + a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT); + a_Pkt.WriteVarInt32(static_cast(Horse.GetHorseType())); + + 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()); + 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()); + + 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: + { + 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: + { + auto & Zombie = reinterpret_cast(a_Mob); + a_Pkt.WriteBEUInt8(AGEABLE_BABY); + a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL); + a_Pkt.WriteBool(Zombie.IsBaby()); + + a_Pkt.WriteBEUInt8(ZOMBIE_TYPE); + a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT); + a_Pkt.WriteVarInt32(Zombie.IsVillagerZombie() ? 1 : 0); // TODO: This actually encodes the zombie villager profession, but that isn't implemented yet. + + 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()) +} diff --git a/src/Protocol/Protocol110x.h b/src/Protocol/Protocol110x.h new file mode 100644 index 000000000..64ef8acb5 --- /dev/null +++ b/src/Protocol/Protocol110x.h @@ -0,0 +1,34 @@ + +// Protocol110x.h + +/* +Declares the 1.10.x protocol classes: + - cProtocol1100 + - release 1.10.0 protocol (#210) +(others may be added later in the future for the 1.10 release series) +*/ + + + + + +#pragma once + +#include "Protocol19x.h" + +class cProtocol1100 : + public cProtocol194 +{ + typedef cProtocol194 super; + +public: + cProtocol1100(cClientHandle * a_Client, const AString &a_ServerAddress, UInt16 a_ServerPort, UInt32 a_State); + + virtual void SendSoundEffect(const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) override; + + virtual void HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer) override; + +protected: + virtual void WriteEntityMetadata(cPacketizer & a_Pkt, const cEntity & a_Entity) override; + virtual void WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_Mob) override; +}; diff --git a/src/Protocol/Protocol19x.cpp b/src/Protocol/Protocol19x.cpp index 456ca8a91..6791da8cd 100644 --- a/src/Protocol/Protocol19x.cpp +++ b/src/Protocol/Protocol19x.cpp @@ -739,7 +739,7 @@ void cProtocol190::SendPickupSpawn(const cPickup & a_Pickup) { ASSERT(m_State == 3); // In game mode? - { + { // TODO Use SendSpawnObject cPacketizer Pkt(*this, 0x00); // Spawn Object packet Pkt.WriteVarInt32(a_Pickup.GetUniqueID()); // TODO: Bad way to write a UUID, and it's not a true UUID, but this is functional for now. @@ -757,14 +757,7 @@ void cProtocol190::SendPickupSpawn(const cPickup & a_Pickup) Pkt.WriteBEInt16(0); } - { - cPacketizer Pkt(*this, 0x39); // Entity Metadata packet - Pkt.WriteVarInt32(a_Pickup.GetUniqueID()); - Pkt.WriteBEUInt8(5); // Index 5: Item - Pkt.WriteBEUInt8(METADATA_TYPE_ITEM); - WriteItem(Pkt, a_Pickup.GetItem()); - Pkt.WriteBEUInt8(0xff); // End of metadata - } + SendEntityMetadata(a_Pickup); } @@ -1057,12 +1050,7 @@ void cProtocol190::SendPlayerSpawn(const cPlayer & a_Player) Pkt.WriteBEDouble(a_Player.GetPosZ()); Pkt.WriteByteAngle(a_Player.GetYaw()); Pkt.WriteByteAngle(a_Player.GetPitch()); - Pkt.WriteBEUInt8(6); // Start metadata - Index 6: Health - Pkt.WriteBEUInt8(METADATA_TYPE_FLOAT); - Pkt.WriteBEFloat(static_cast(a_Player.GetHealth())); - Pkt.WriteBEUInt8(2); // Index 2: Custom name - Pkt.WriteBEUInt8(METADATA_TYPE_STRING); - Pkt.WriteString(a_Player.GetName()); + WriteEntityMetadata(Pkt, a_Player); Pkt.WriteBEUInt8(0xff); // Metadata: end } @@ -3520,7 +3508,22 @@ void cProtocol190::WriteEntityMetadata(cPacketizer & a_Pkt, const cEntity & a_En switch (a_Entity.GetEntityType()) { - case cEntity::etPlayer: break; // TODO? + 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(2); // Index 2: Custom name + a_Pkt.WriteBEUInt8(METADATA_TYPE_STRING); + a_Pkt.WriteString(Player.GetName()); + + a_Pkt.WriteBEUInt8(6); // Start metadata - Index 6: Health + a_Pkt.WriteBEUInt8(METADATA_TYPE_FLOAT); + a_Pkt.WriteBEFloat(static_cast(Player.GetHealth())); + break; + } case cEntity::etPickup: { a_Pkt.WriteBEUInt8(5); // Index 5: Item @@ -4367,8 +4370,3 @@ void cProtocol194::SendUpdateSign(int a_BlockX, int a_BlockY, int a_BlockZ, cons Writer.Finish(); Pkt.WriteBuf(Writer.GetResult().data(), Writer.GetResult().size()); } - - - - - diff --git a/src/Protocol/Protocol19x.h b/src/Protocol/Protocol19x.h index 9124a5422..79180e3a7 100644 --- a/src/Protocol/Protocol19x.h +++ b/src/Protocol/Protocol19x.h @@ -259,10 +259,10 @@ protected: void WriteItem(cPacketizer & a_Pkt, const cItem & a_Item); /** Writes the metadata for the specified entity, not including the terminating 0xff. */ - void WriteEntityMetadata(cPacketizer & a_Pkt, const cEntity & a_Entity); + virtual void WriteEntityMetadata(cPacketizer & a_Pkt, const cEntity & a_Entity); /** Writes the mob-specific metadata for the specified mob */ - void WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_Mob); + virtual void WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_Mob); /** Writes the entity properties for the specified entity, including the Count field. */ void WriteEntityProperties(cPacketizer & a_Pkt, const cEntity & a_Entity); diff --git a/src/Protocol/ProtocolRecognizer.cpp b/src/Protocol/ProtocolRecognizer.cpp index 8b05ce768..ca0d05c51 100644 --- a/src/Protocol/ProtocolRecognizer.cpp +++ b/src/Protocol/ProtocolRecognizer.cpp @@ -9,6 +9,7 @@ #include "ProtocolRecognizer.h" #include "Protocol18x.h" #include "Protocol19x.h" +#include "Protocol110x.h" #include "Packetizer.h" #include "../ClientHandle.h" #include "../Root.h" @@ -47,11 +48,12 @@ AString cProtocolRecognizer::GetVersionTextFromInt(int a_ProtocolVersion) { switch (a_ProtocolVersion) { - case PROTO_VERSION_1_8_0: return "1.8"; - case PROTO_VERSION_1_9_0: return "1.9"; - case PROTO_VERSION_1_9_1: return "1.9.1"; - case PROTO_VERSION_1_9_2: return "1.9.2"; - case PROTO_VERSION_1_9_4: return "1.9.4"; + case PROTO_VERSION_1_8_0: return "1.8"; + case PROTO_VERSION_1_9_0: return "1.9"; + case PROTO_VERSION_1_9_1: return "1.9.1"; + case PROTO_VERSION_1_9_2: return "1.9.2"; + case PROTO_VERSION_1_9_4: return "1.9.4"; + case PROTO_VERSION_1_10_0: return "1.10"; } ASSERT(!"Unknown protocol version"); return Printf("Unknown protocol (%d)", a_ProtocolVersion); @@ -1047,6 +1049,11 @@ bool cProtocolRecognizer::TryRecognizeLengthedProtocol(UInt32 a_PacketLengthRema m_Protocol = new cProtocol194(m_Client, ServerAddress, ServerPort, NextState); return true; } + case PROTO_VERSION_1_10_0: + { + m_Protocol = new cProtocol1100(m_Client, ServerAddress, ServerPort, NextState); + return true; + } default: { LOGINFO("Client \"%s\" uses an unsupported protocol (lengthed, version %u (0x%x))", diff --git a/src/Protocol/ProtocolRecognizer.h b/src/Protocol/ProtocolRecognizer.h index 5f0fa2dcb..6390b6289 100644 --- a/src/Protocol/ProtocolRecognizer.h +++ b/src/Protocol/ProtocolRecognizer.h @@ -18,8 +18,8 @@ // Adjust these if a new protocol is added or an old one is removed: -#define MCS_CLIENT_VERSIONS "1.8.x, 1.9.x" -#define MCS_PROTOCOL_VERSIONS "47, 107, 108, 109, 110" +#define MCS_CLIENT_VERSIONS "1.8.x, 1.9.x, 1.10.x" +#define MCS_PROTOCOL_VERSIONS "47, 107, 108, 109, 110, 210" @@ -33,11 +33,12 @@ class cProtocolRecognizer : public: enum { - PROTO_VERSION_1_8_0 = 47, - PROTO_VERSION_1_9_0 = 107, - PROTO_VERSION_1_9_1 = 108, - PROTO_VERSION_1_9_2 = 109, - PROTO_VERSION_1_9_4 = 110, + PROTO_VERSION_1_8_0 = 47, + PROTO_VERSION_1_9_0 = 107, + PROTO_VERSION_1_9_1 = 108, + PROTO_VERSION_1_9_2 = 109, + PROTO_VERSION_1_9_4 = 110, + PROTO_VERSION_1_10_0 = 210, } ; cProtocolRecognizer(cClientHandle * a_Client);