diff --git a/MinecraftClient/ChatBot.cs b/MinecraftClient/ChatBot.cs index cca54636..7f88fa1e 100644 --- a/MinecraftClient/ChatBot.cs +++ b/MinecraftClient/ChatBot.cs @@ -333,9 +333,17 @@ namespace MinecraftClient /// /// Called when the health of an entity changed /// - /// Entity ID + /// Entity /// The health of the entity - public virtual void OnEntityHealth(int entityID, float health) { } + public virtual void OnEntityHealth(Entity entity, float health) { } + + /// + /// Called when the metadata of an entity changed + /// + /// Entity + /// The metadata of the entity + /// Ptotocol version + public virtual void OnEntityMetadata(Entity entity, Dictionary metadata) { } /* =================================================================== */ /* ToolBox - Methods below might be useful while creating your bot. */ diff --git a/MinecraftClient/Commands/Entitycmd.cs b/MinecraftClient/Commands/Entitycmd.cs index a89924fd..908bff39 100644 --- a/MinecraftClient/Commands/Entitycmd.cs +++ b/MinecraftClient/Commands/Entitycmd.cs @@ -3,13 +3,14 @@ using System.Collections.Generic; using System.Linq; using System.Text; using MinecraftClient.Inventory; +using MinecraftClient.Mapping; namespace MinecraftClient.Commands { class Entitycmd : Command { public override string CMDName { get { return "entity"; } } - public override string CMDDesc { get { return "entity "; } } + public override string CMDDesc { get { return "entity "; } } public override string Run(McClient handler, string command, Dictionary localVars) { @@ -20,21 +21,55 @@ namespace MinecraftClient.Commands { try { - int entityID; - entityID = int.Parse(args[0]); - string action = args.Length > 1 - ? args[1].ToLower() - : "list"; - switch (action) + int entityID = 0; + int.TryParse(args[0], out entityID); + if (entityID != 0) { - case "attack": - handler.InteractEntity(entityID, 1); - return "Entity attacked"; - case "use": - handler.InteractEntity(entityID, 0); - return "Entity used"; - default: - return CMDDesc; + string action = args.Length > 1 + ? args[1].ToLower() + : "list"; + switch (action) + { + case "attack": + handler.InteractEntity(entityID, 1); + return "Entity attacked"; + case "use": + handler.InteractEntity(entityID, 0); + return "Entity used"; + default: + return CMDDesc; + } + } + else + { + EntityType interacttype = EntityType.Player; + Enum.TryParse(args[0], out interacttype); + Dictionary entities = handler.GetEntities(); + string actionst = "Entity attacked"; + int actioncount = 0; + foreach (var entity2 in entities) + { + if (entity2.Value.Type == interacttype) + { + string action = args.Length > 1 + ? args[1].ToLower() + : "list"; + if (action == "attack") + { + handler.InteractEntity(entity2.Key, 1); + actionst = "Entity attacked"; + actioncount++; + } + else if (action == "use") + { + handler.InteractEntity(entity2.Key, 0); + actionst = "Entity used"; + actioncount++; + } + else return CMDDesc; + } + } + return actioncount + " " + actionst; } } catch (FormatException) { return CMDDesc; } @@ -46,10 +81,12 @@ namespace MinecraftClient.Commands response.Add("Entities:"); foreach (var entity2 in entities) { - if (entity2.Value.Type == Mapping.EntityType.Player) - response.Add(String.Format(" #{0}: {1} | {2}", entity2.Key, entity2.Value.Type, entity2.Value.Name)); + if (entity2.Value.Type == EntityType.Item || entity2.Value.Type == EntityType.ItemFrame || entity2.Value.Type == Mapping.EntityType.EyeOfEnder || entity2.Value.Type == Mapping.EntityType.Egg || entity2.Value.Type == Mapping.EntityType.EnderPearl || entity2.Value.Type == Mapping.EntityType.Potion || entity2.Value.Type == Mapping.EntityType.Fireball || entity2.Value.Type == Mapping.EntityType.FireworkRocket) + response.Add(String.Format(" #{0}: Type: {1}, Item: {2}, Location: {3}", entity2.Key, entity2.Value.Type, entity2.Value.Item.Type, entity2.Value.Location)); + else if (entity2.Value.Type == Mapping.EntityType.Player && entity2.Value.Name != string.Empty) + response.Add(String.Format(" #{0}: Type: {1}, Nickname: {2}, Latency: {3}, Health: {4}, Pose: {5}, Location: {6}", entity2.Key, entity2.Value.Type, entity2.Value.Name, entity2.Value.Latency, entity2.Value.Health, entity2.Value.Pose, entity2.Value.Location)); else - response.Add(String.Format(" #{0}: {1}", entity2.Key, entity2.Value.Type)); + response.Add(String.Format(" #{0}: Type: {1}, Health: {2}, Location: {3}", entity2.Key, entity2.Value.Type, entity2.Value.Health, entity2.Value.Location)); } response.Add(CMDDesc); return String.Join("\n", response); diff --git a/MinecraftClient/Mapping/Entity.cs b/MinecraftClient/Mapping/Entity.cs index e1b86ee3..0a02aea8 100644 --- a/MinecraftClient/Mapping/Entity.cs +++ b/MinecraftClient/Mapping/Entity.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using MinecraftClient.Inventory; namespace MinecraftClient.Mapping { @@ -22,6 +23,26 @@ namespace MinecraftClient.Mapping /// Nickname of the entity if it is a player. /// public string Name; + + /// + /// CustomName of the entity. + /// + public string CustomNameJson; + + /// + /// IsCustomNameVisible of the entity. + /// + public bool IsCustomNameVisible; + + /// + /// CustomName of the entity. + /// + public string CustomName; + + /// + /// Latency of the entity if it is a player. + /// + public int Latency; /// /// Entity type @@ -37,7 +58,22 @@ namespace MinecraftClient.Mapping /// Health of the entity /// public float Health; - + + /// + /// Item of the entity if ItemFrame or Item + /// + public Item Item; + + /// + /// Entity pose in the Minecraft world + /// + public EntityPose Pose; + + /// + /// Entity metadata + /// + public Dictionary Metadata; + /// /// Create a new entity based on Entity ID, Entity Type and location /// diff --git a/MinecraftClient/Mapping/EntityPose.cs b/MinecraftClient/Mapping/EntityPose.cs new file mode 100644 index 00000000..28af6e3a --- /dev/null +++ b/MinecraftClient/Mapping/EntityPose.cs @@ -0,0 +1,13 @@ +namespace MinecraftClient.Mapping +{ + public enum EntityPose + { + Standing = 0, + FallFlying = 1, + Sleeping = 2, + Swimming = 3, + SpinAttack = 4, + Sneaking = 5, + Dying = 6, + } +} diff --git a/MinecraftClient/Mapping/EntityTypeExtensions.cs b/MinecraftClient/Mapping/EntityTypeExtensions.cs index a29f5e0a..7f2205d2 100644 --- a/MinecraftClient/Mapping/EntityTypeExtensions.cs +++ b/MinecraftClient/Mapping/EntityTypeExtensions.cs @@ -47,5 +47,27 @@ namespace MinecraftClient.Mapping return false; } } + + /// + /// Indicates whether the entity type contains an inner item + /// + /// TRUE if item holder (Item Entity, ItemFrame...) + public static bool ContainsItem(this EntityType e) + { + switch (e) + { + case EntityType.Item: + case EntityType.ItemFrame: + case EntityType.EyeOfEnder: + case EntityType.Egg: + case EntityType.EnderPearl: + case EntityType.Potion: + case EntityType.Fireball: + case EntityType.FireworkRocket: + return true; + default: + return false; + }; + } } } diff --git a/MinecraftClient/McClient.cs b/MinecraftClient/McClient.cs index 1758a7cd..14eb2aa2 100644 --- a/MinecraftClient/McClient.cs +++ b/MinecraftClient/McClient.cs @@ -1530,6 +1530,7 @@ namespace MinecraftClient world.Clear(); } + entities.Clear(); ClearInventories(); DispatchBotEvent(bot => bot.OnRespawn()); } @@ -2071,6 +2072,14 @@ namespace MinecraftClient { playerName = onlinePlayers[uuid]; playersLatency[playerName] = latency; + foreach (KeyValuePair ent in entities) + { + if (ent.Value.UUID == uuid && ent.Value.Name == playerName) + { + ent.Value.Latency = latency; + break; + } + } DispatchBotEvent(bot => bot.OnLatencyUpdate(playerName, uuid, latency)); } } @@ -2149,7 +2158,49 @@ namespace MinecraftClient if (entities.ContainsKey(entityID)) { entities[entityID].Health = health; - DispatchBotEvent(bot => bot.OnEntityHealth(entityID, health)); + DispatchBotEvent(bot => bot.OnEntityHealth(entities[entityID], health)); + } + } + + /// + /// Called when the metadata of an entity changed + /// + /// Entity ID + /// The metadata of the entity + public void OnEntityMetadata(int entityID, Dictionary metadata) + { + if (entities.ContainsKey(entityID)) + { + Entity entity = entities[entityID]; + try + { + entity.Metadata = metadata; + if (entity.Type.ContainsItem() && metadata.ContainsKey(7) && metadata[7] != null && metadata[7].GetType() == typeof(Item)) + { + try + { + entity.Item = (Item)metadata[7]; + } + catch + { + entity.Item = new Item(ItemType.Air, 1, null); + } + } + if (metadata.ContainsKey(6) && metadata[6].GetType() == typeof(Int32)) + { + entity.Pose = (EntityPose)metadata[6]; + } + if (metadata.ContainsKey(2) && metadata.ContainsValue(metadata[2]) && metadata[2].GetType() == typeof(string)) + { + entity.CustomNameJson = metadata[2].ToString(); + entity.CustomName = ChatParser.ParseText(metadata[2].ToString()); + } + if (metadata.ContainsKey(3) && metadata.ContainsValue(metadata[3]) && metadata[3].GetType() == typeof(bool)) + { + entity.IsCustomNameVisible = (bool)metadata[3]; + } + DispatchBotEvent(bot => bot.OnEntityMetadata(entity, metadata)); + } catch { } } } #endregion diff --git a/MinecraftClient/MinecraftClient.csproj b/MinecraftClient/MinecraftClient.csproj index 7e3d5727..20bdfd58 100644 --- a/MinecraftClient/MinecraftClient.csproj +++ b/MinecraftClient/MinecraftClient.csproj @@ -144,6 +144,7 @@ + @@ -334,4 +335,4 @@ --> - \ No newline at end of file + diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index 7d1b1beb..5398c2e9 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -909,10 +909,11 @@ namespace MinecraftClient.Protocol.Handlers if (handler.GetEntityHandlingEnabled()) { int EntityID = dataTypes.ReadNextVarInt(packetData); - Dictionary metadata = dataTypes.ReadNextMetadata(packetData, itemPalette); // need itemPalette because metadata need to read slot item + Dictionary metadata = dataTypes.ReadNextMetadata(packetData, itemPalette); int healthField = protocolversion >= MC114Version ? 8 : 7; // Health is field no. 7 in 1.10+ and 8 in 1.14+ if (metadata.ContainsKey(healthField) && metadata[healthField].GetType() == typeof(float)) handler.OnEntityHealth(EntityID, (float)metadata[healthField]); + handler.OnEntityMetadata(EntityID, metadata); } break; case PacketIncomingType.TimeUpdate: diff --git a/MinecraftClient/Protocol/IMinecraftComHandler.cs b/MinecraftClient/Protocol/IMinecraftComHandler.cs index 3e6740f9..289daed4 100644 --- a/MinecraftClient/Protocol/IMinecraftComHandler.cs +++ b/MinecraftClient/Protocol/IMinecraftComHandler.cs @@ -221,6 +221,20 @@ namespace MinecraftClient.Protocol /// void OnUpdateHealth(float health, int food); + /// + /// Called when the health of an entity changed + /// + /// Entity ID + /// The health of the entity + void OnEntityHealth(int entityID, float health); + + /// + /// Called when entity metadata or metadata changed. + /// + /// Entity ID + /// Entity metadata + void OnEntityMetadata(int EntityID, Dictionary metadata); + /// /// Called when and explosion occurs on the server /// @@ -301,12 +315,5 @@ namespace MinecraftClient.Protocol /// The name of the objective the score belongs to /// he score to be displayed next to the entry. Only sent when Action does not equal 1. void OnUpdateScore(string entityname, byte action, string objectivename, int value); - - /// - /// Called when the health of an entity changed - /// - /// Entity ID - /// The health of the entity - void OnEntityHealth(int entityID, float health); } }