void cProtocol125::WriteEntityMetadata(const cEntity & a_Entity) { if (a_Entity.IsMinecart()) { WriteByte(0x51); // No idea how Mojang makes their carts shakey shakey, so here is a complicated one-liner expression that does something similar WriteInt( (((a_Entity.GetMaxHealth() / 2) - (a_Entity.GetHealth() - (a_Entity.GetMaxHealth() / 2))) * ((const cMinecart &)a_Entity).LastDamage()) * 4 ); WriteByte(0x52); WriteInt(1); // Shaking direction, doesn't seem to affect anything WriteByte(0x73); WriteFloat((float)(((const cMinecart &)a_Entity).LastDamage() + 10)); // Damage taken / shake effect multiplyer if (((cMinecart &)a_Entity).GetPayload() == cMinecart::mpFurnace) { WriteByte(0x10); WriteByte(((const cMinecartWithFurnace &)a_Entity).IsFueled() ? 1 : 0); // Fueled? } } else if ((a_Entity.IsProjectile() && ((cProjectileEntity &)a_Entity).GetProjectileKind() == cProjectileEntity::pkArrow)) { WriteByte(0x10); WriteByte(((const cArrowEntity &)a_Entity).IsCritical() ? 1 : 0); // Critical hitting arrow? } }
void cProtocol_1_11_0::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; } } }