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);
}
}