From bd85c4666329b6a078b54e2298cd640d9c0824a7 Mon Sep 17 00:00:00 2001 From: ORelio Date: Sun, 24 May 2020 18:21:22 +0200 Subject: [PATCH] Implement entity types (#1001) Implement palette generation and investigate palette changes between versions. Turns out 1.13- has legacy IDs, 1.14 switches to entity palette and 1.15 refreshes the whole palette just to insert Bee. Also refactor entity handling code here and there. --- MinecraftClient/ChatBots/AutoAttack.cs | 2 +- MinecraftClient/ChatBots/AutoFishing.cs | 4 +- .../Inventory/ItemTypeGenerator.cs | 44 +---- MinecraftClient/Mapping/Block.cs | 2 +- ...BlockPaletteMapping.cs => BlockPalette.cs} | 2 +- .../Mapping/BlockPalettes/Palette112.cs | 2 +- .../Mapping/BlockPalettes/Palette113.cs | 2 +- .../Mapping/BlockPalettes/Palette114.cs | 2 +- .../Mapping/BlockPalettes/Palette115.cs | 2 +- MinecraftClient/Mapping/Entity.cs | 83 +--------- .../Mapping/EntityPalettes/EntityPalette.cs | 51 ++++++ .../EntityPalettes/EntityPalette113.cs | 156 ++++++++++++++++++ .../EntityPalettes/EntityPalette114.cs | 125 ++++++++++++++ .../EntityPalettes/EntityPalette115.cs | 126 ++++++++++++++ .../EntityPalettes/EntityPaletteGenerator.cs | 20 +++ .../Mapping/EntityPalettes/EntityType114.cs | 108 ++++++++++++ .../Mapping/EntityPalettes/EntityType115.cs | 109 ++++++++++++ MinecraftClient/Mapping/EntityType.cs | 121 +++++++++++++- .../Mapping/EntityTypeExtensions.cs | 48 ++++++ MinecraftClient/McTcpClient.cs | 45 +---- MinecraftClient/MinecraftClient.csproj | 9 +- MinecraftClient/Program.cs | 1 + MinecraftClient/Protocol/DataTypeGenerator.cs | 144 ++++++++++++++++ .../Protocol/Handlers/DataTypes.cs | 43 ++++- .../Protocol/Handlers/Protocol18.cs | 63 +++---- .../Handlers/Protocol18PacketTypes.cs | 28 ++-- .../Protocol/IMinecraftComHandler.cs | 30 +--- 27 files changed, 1113 insertions(+), 259 deletions(-) rename MinecraftClient/Mapping/BlockPalettes/{BlockPaletteMapping.cs => BlockPalette.cs} (96%) create mode 100644 MinecraftClient/Mapping/EntityPalettes/EntityPalette.cs create mode 100644 MinecraftClient/Mapping/EntityPalettes/EntityPalette113.cs create mode 100644 MinecraftClient/Mapping/EntityPalettes/EntityPalette114.cs create mode 100644 MinecraftClient/Mapping/EntityPalettes/EntityPalette115.cs create mode 100644 MinecraftClient/Mapping/EntityPalettes/EntityPaletteGenerator.cs create mode 100644 MinecraftClient/Mapping/EntityPalettes/EntityType114.cs create mode 100644 MinecraftClient/Mapping/EntityPalettes/EntityType115.cs create mode 100644 MinecraftClient/Mapping/EntityTypeExtensions.cs create mode 100644 MinecraftClient/Protocol/DataTypeGenerator.cs diff --git a/MinecraftClient/ChatBots/AutoAttack.cs b/MinecraftClient/ChatBots/AutoAttack.cs index 6ccd9f58..99ccac3b 100644 --- a/MinecraftClient/ChatBots/AutoAttack.cs +++ b/MinecraftClient/ChatBots/AutoAttack.cs @@ -112,7 +112,7 @@ namespace MinecraftClient.ChatBots /// If the entity should be attacked public bool handleEntity(Entity entity) { - if (!entity.IsHostile()) + if (!entity.Type.IsHostile()) return false; bool isBeingAttacked = entitiesToAttack.ContainsKey(entity.ID); diff --git a/MinecraftClient/ChatBots/AutoFishing.cs b/MinecraftClient/ChatBots/AutoFishing.cs index 701ad180..e5e91adb 100644 --- a/MinecraftClient/ChatBots/AutoFishing.cs +++ b/MinecraftClient/ChatBots/AutoFishing.cs @@ -46,7 +46,7 @@ namespace MinecraftClient.ChatBots public override void OnEntitySpawn(Entity entity) { - if (entity.TypeID == 102) + if (entity.Type == EntityType.FishingBobber) { if (GetCurrentLocation().Distance(entity.Location) < 2 && !isFishing) { @@ -60,7 +60,7 @@ namespace MinecraftClient.ChatBots public override void OnEntityDespawn(Entity entity) { - if(entity.TypeID == 102 && isFishing) + if (entity.Type == EntityType.FishingBobber) { if(entity.ID == fishingRod.ID) { diff --git a/MinecraftClient/Inventory/ItemTypeGenerator.cs b/MinecraftClient/Inventory/ItemTypeGenerator.cs index fa99d3cb..a056b7f0 100644 --- a/MinecraftClient/Inventory/ItemTypeGenerator.cs +++ b/MinecraftClient/Inventory/ItemTypeGenerator.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.IO; +using MinecraftClient.Protocol; namespace MinecraftClient.Inventory { @@ -14,45 +11,10 @@ namespace MinecraftClient.Inventory /// Generate ItemType.cs from Minecraft registries.json /// /// path to registries.json - /// output path for ItemTypes.cs /// java -cp minecraft_server.jar net.minecraft.data.Main --reports - public static void JsonToClass(string registriesJsonFile, string outputEnum) + public static void GenerateItemTypes(string registriesJsonFile) { - HashSet itemIds = new HashSet(); - Json.JSONData registries = Json.ParseJson(File.ReadAllText(registriesJsonFile)); - Json.JSONData itemRegistry = registries.Properties["minecraft:item"].Properties["entries"]; - List outFile = new List(); - - outFile.AddRange(new[] { - "namespace MinecraftClient.Inventory", - "{", - " public enum ItemType", - " {" - }); - - foreach (KeyValuePair item in itemRegistry.Properties) - { - int itemId = int.Parse(item.Value.Properties["protocol_id"].StringValue); - - //minecraft:item_name => ItemName - string itemName = String.Concat( - item.Key.Replace("minecraft:", "") - .Split('_') - .Select(word => char.ToUpper(word[0]) + word.Substring(1)) - ); - - if (itemIds.Contains(itemId)) - throw new InvalidDataException("Duplicate item ID " + itemId + "!?"); - - outFile.Add(" " + itemName + " = " + itemId + ','); - } - - outFile.AddRange(new[] { - " }", - "}" - }); - - File.WriteAllLines(outputEnum, outFile); + DataTypeGenerator.GenerateEnum(registriesJsonFile, "minecraft:item", "ItemType", "MinecraftClient.Inventory"); } } } diff --git a/MinecraftClient/Mapping/Block.cs b/MinecraftClient/Mapping/Block.cs index 7d835565..0a9eb382 100644 --- a/MinecraftClient/Mapping/Block.cs +++ b/MinecraftClient/Mapping/Block.cs @@ -15,7 +15,7 @@ namespace MinecraftClient.Mapping /// Get or set global block ID to Material mapping /// The global Palette is a concept introduced with Minecraft 1.13 /// - public static BlockPaletteMapping Palette { get; set; } + public static BlockPalette Palette { get; set; } /// /// Storage for block ID and metadata, as ushort for compatibility, performance and lower memory footprint diff --git a/MinecraftClient/Mapping/BlockPalettes/BlockPaletteMapping.cs b/MinecraftClient/Mapping/BlockPalettes/BlockPalette.cs similarity index 96% rename from MinecraftClient/Mapping/BlockPalettes/BlockPaletteMapping.cs rename to MinecraftClient/Mapping/BlockPalettes/BlockPalette.cs index 717f5cb5..9b8611b6 100644 --- a/MinecraftClient/Mapping/BlockPalettes/BlockPaletteMapping.cs +++ b/MinecraftClient/Mapping/BlockPalettes/BlockPalette.cs @@ -5,7 +5,7 @@ using System.Text; namespace MinecraftClient.Mapping.BlockPalettes { - public abstract class BlockPaletteMapping + public abstract class BlockPalette { /// /// Get mapping dictionary. Must be overriden with proper implementation. diff --git a/MinecraftClient/Mapping/BlockPalettes/Palette112.cs b/MinecraftClient/Mapping/BlockPalettes/Palette112.cs index e0f7438d..7afc42ad 100644 --- a/MinecraftClient/Mapping/BlockPalettes/Palette112.cs +++ b/MinecraftClient/Mapping/BlockPalettes/Palette112.cs @@ -9,7 +9,7 @@ namespace MinecraftClient.Mapping.BlockPalettes /// Some blocks previously had different IDs depending on state, they have been merged here /// Comments correspond to changed material names since previous MCC versions /// - public class Palette112 : BlockPaletteMapping + public class Palette112 : BlockPalette { private static Dictionary materials = new Dictionary() { diff --git a/MinecraftClient/Mapping/BlockPalettes/Palette113.cs b/MinecraftClient/Mapping/BlockPalettes/Palette113.cs index 18c7db37..ca29cfab 100644 --- a/MinecraftClient/Mapping/BlockPalettes/Palette113.cs +++ b/MinecraftClient/Mapping/BlockPalettes/Palette113.cs @@ -7,7 +7,7 @@ namespace MinecraftClient.Mapping.BlockPalettes /// Defines mappings for Minecraft 1.13. /// Automatically generated using BlockPaletteGenerator.cs /// - public class Palette113 : BlockPaletteMapping + public class Palette113 : BlockPalette { private static Dictionary materials = new Dictionary(); diff --git a/MinecraftClient/Mapping/BlockPalettes/Palette114.cs b/MinecraftClient/Mapping/BlockPalettes/Palette114.cs index ea0bab3e..2503f1bb 100644 --- a/MinecraftClient/Mapping/BlockPalettes/Palette114.cs +++ b/MinecraftClient/Mapping/BlockPalettes/Palette114.cs @@ -7,7 +7,7 @@ namespace MinecraftClient.Mapping.BlockPalettes /// Defines mappings for Minecraft 1.14. /// Automatically generated using BlockPaletteGenerator.cs /// - public class Palette114 : BlockPaletteMapping + public class Palette114 : BlockPalette { private static Dictionary materials = new Dictionary(); diff --git a/MinecraftClient/Mapping/BlockPalettes/Palette115.cs b/MinecraftClient/Mapping/BlockPalettes/Palette115.cs index bb823763..b56accc1 100644 --- a/MinecraftClient/Mapping/BlockPalettes/Palette115.cs +++ b/MinecraftClient/Mapping/BlockPalettes/Palette115.cs @@ -7,7 +7,7 @@ namespace MinecraftClient.Mapping.BlockPalettes /// Defines mappings for Minecraft 1.15. /// Automatically generated using BlockPaletteGenerator.cs /// - public class Palette115 : BlockPaletteMapping + public class Palette115 : BlockPalette { private static Dictionary materials = new Dictionary(); diff --git a/MinecraftClient/Mapping/Entity.cs b/MinecraftClient/Mapping/Entity.cs index 9feae262..011cb912 100644 --- a/MinecraftClient/Mapping/Entity.cs +++ b/MinecraftClient/Mapping/Entity.cs @@ -21,60 +21,15 @@ namespace MinecraftClient.Mapping public Guid UUID; /// - /// Entity type determined by Minecraft Console Client + /// Entity type /// public EntityType Type; - /// - /// Entity type ID (more precise than Type, but may change between Minecraft versions) - /// - public int TypeID; - /// /// Entity location in the Minecraft world /// public Location Location; - /// - /// Create a new entity based on Entity ID and location - /// - /// Entity ID - /// Entity location - public Entity(int ID, Location location) - { - this.ID = ID; - this.Location = location; - } - - /// - /// Create a new entity based on Entity ID, Entity Type and location - /// - /// Entity ID - /// Entity Type ID - /// Entity location - public Entity(int ID, int TypeID, Location location) - { - this.ID = ID; - this.TypeID = TypeID; - this.Location = location; - } - - /// - /// Create a new entity based on Entity ID, Entity Type, location, and UUID - /// - /// Entity ID - /// Entity Type ID - /// Entity Type Enum - /// Entity location - public Entity(int ID, int TypeID, EntityType type, Location location, Guid uuid) - { - this.ID = ID; - this.TypeID = TypeID; - this.Type = type; - this.Location = location; - this.UUID = uuid; - } - /// /// Create a new entity based on Entity ID, Entity Type and location /// @@ -100,41 +55,5 @@ namespace MinecraftClient.Mapping this.Location = location; this.UUID = uuid; } - - /// - /// Return TRUE if the Entity is an hostile mob - /// - /// New mobs added in newer Minecraft versions might be absent from the list - /// TRUE if hostile - public bool IsHostile() - { - switch (TypeID) - { - case 5: return true; // Blaze; - case 12: return true; // Creeper - case 16: return true; // Drowned - case 23: return true; // Evoker - case 29: return true; // Ghast - case 31: return true; // Guardian - case 33: return true; // Husk - case 41: return true; // Magma Cube - case 57: return true; // Zombie Pigman - case 63: return true; // Shulker - case 65: return true; // Silverfish - case 66: return true; // Skeleton - case 68: return true; // Slime - case 75: return true; // Stray - case 84: return true; // Vex - case 87: return true; // Vindicator - case 88: return true; // Pillager - case 90: return true; // Witch - case 92: return true; // Wither Skeleton - case 95: return true; // Zombie - case 97: return true; // Zombie Villager - case 98: return true; // Phantom - case 99: return true; // Ravager - default: return false; - } - } } } diff --git a/MinecraftClient/Mapping/EntityPalettes/EntityPalette.cs b/MinecraftClient/Mapping/EntityPalettes/EntityPalette.cs new file mode 100644 index 00000000..9b29f460 --- /dev/null +++ b/MinecraftClient/Mapping/EntityPalettes/EntityPalette.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MinecraftClient.Mapping.EntityPalettes +{ + public abstract class EntityPalette + { + /// + /// Get mapping dictionary. Must be overriden with proper implementation. + /// + /// Palette dictionary + protected abstract Dictionary GetDict(); + + /// + /// Get mapping dictionary for pre-1.13. May be overriden with proper implementation. + /// + /// Palette dictionary for non-living entities (pre-1.13) + protected virtual Dictionary GetDictNonLiving() + { + return null; + } + + /// + /// Get entity type from type ID + /// + /// Entity type ID + /// EntityType corresponding to the specified ID + public EntityType FromId(int id, bool living) + { + Dictionary entityTypes = GetDict(); + Dictionary entityTypesNonLiving = GetDictNonLiving(); + + if (entityTypesNonLiving != null && !living) + { + //Pre-1.13 non-living entities have a different set of IDs (entityTypesNonLiving != null) + if (entityTypesNonLiving.ContainsKey(id)) + return entityTypesNonLiving[id]; + } + else + { + //Post-1.13 entities have the same set of IDs regardless of living status + if (entityTypes.ContainsKey(id)) + return entityTypes[id]; + } + + throw new System.IO.InvalidDataException("Unknown Entity ID " + id + ". Is Entity Palette up to date for this Minecraft version?"); + } + } +} diff --git a/MinecraftClient/Mapping/EntityPalettes/EntityPalette113.cs b/MinecraftClient/Mapping/EntityPalettes/EntityPalette113.cs new file mode 100644 index 00000000..22135c59 --- /dev/null +++ b/MinecraftClient/Mapping/EntityPalettes/EntityPalette113.cs @@ -0,0 +1,156 @@ +using System; +using System.Collections.Generic; + +namespace MinecraftClient.Mapping.EntityPalettes +{ + /// + /// Defines mappings for pre-1.14 entitiy IDs + /// Pre-1.14 Minecraft has 2 set of ids: One for non-living objects and one for living mobs + /// Post-1.14 Minecraft has only one set of ids for all types of entities + /// + public class EntityPalette113 : EntityPalette + { + private static Dictionary mappingsObjects = new Dictionary() + { + // https://wiki.vg/Entity_metadata#Objects + { 1, EntityType.Boat }, + { 2, EntityType.Item }, + { 3, EntityType.AreaEffectCloud }, + { 10, EntityType.Minecart }, + { 50, EntityType.Tnt }, + { 51, EntityType.EndCrystal }, + { 60, EntityType.Arrow }, + { 61, EntityType.Snowball }, + { 62, EntityType.Egg }, + { 63, EntityType.Fireball }, + { 64, EntityType.SmallFireball }, + { 65, EntityType.EnderPearl }, + { 66, EntityType.WitherSkull }, + { 67, EntityType.ShulkerBullet }, + { 68, EntityType.LlamaSpit }, + { 70, EntityType.FallingBlock }, + { 71, EntityType.ItemFrame }, + { 72, EntityType.EyeOfEnder }, + { 73, EntityType.Potion }, + { 75, EntityType.ExperienceBottle }, + { 76, EntityType.FireworkRocket }, + { 77, EntityType.LeashKnot }, + { 78, EntityType.ArmorStand }, + { 79, EntityType.EvokerFangs }, + { 90, EntityType.FishingBobber }, + { 91, EntityType.SpectralArrow }, + { 93, EntityType.DragonFireball }, + { 94, EntityType.Trident }, + }; + + private static Dictionary mappingsMobs = new Dictionary() + { + // https://wiki.vg/Entity_metadata#Mobs + { 0, EntityType.AreaEffectCloud }, + { 1, EntityType.ArmorStand }, + { 2, EntityType.Arrow }, + { 3, EntityType.Bat }, + { 4, EntityType.Blaze }, + { 5, EntityType.Boat }, + { 6, EntityType.CaveSpider }, + { 7, EntityType.Chicken }, + { 8, EntityType.Cod }, + { 9, EntityType.Cow }, + { 10, EntityType.Creeper }, + { 11, EntityType.Donkey }, + { 12, EntityType.Dolphin }, + { 13, EntityType.DragonFireball }, + { 14, EntityType.Drowned }, + { 15, EntityType.ElderGuardian }, + { 16, EntityType.EndCrystal }, + { 17, EntityType.EnderDragon }, + { 18, EntityType.Enderman }, + { 19, EntityType.Endermite }, + { 20, EntityType.EvokerFangs }, + { 21, EntityType.Evoker }, + { 22, EntityType.ExperienceBottle }, + { 23, EntityType.EyeOfEnder }, + { 24, EntityType.FallingBlock }, + { 25, EntityType.FireworkRocket }, + { 26, EntityType.Ghast }, + { 27, EntityType.Giant }, + { 28, EntityType.Guardian }, + { 29, EntityType.Horse }, + { 30, EntityType.Husk }, + { 31, EntityType.Illusioner }, + { 32, EntityType.Item }, + { 33, EntityType.ItemFrame }, + { 34, EntityType.Fireball }, + { 35, EntityType.LeashKnot }, + { 36, EntityType.Llama }, + { 37, EntityType.LlamaSpit }, + { 38, EntityType.MagmaCube }, + { 39, EntityType.Minecart }, + { 40, EntityType.ChestMinecart }, + { 41, EntityType.CommandBlockMinecart }, + { 42, EntityType.FurnaceMinecart }, + { 43, EntityType.HopperMinecart }, + { 44, EntityType.SpawnerMinecart }, + { 45, EntityType.TntMinecart }, + { 46, EntityType.Mule }, + { 47, EntityType.Mooshroom }, + { 48, EntityType.Ocelot }, + { 49, EntityType.Painting }, + { 50, EntityType.Parrot }, + { 51, EntityType.Pig }, + { 52, EntityType.Pufferfish }, + { 53, EntityType.ZombiePigman }, + { 54, EntityType.PolarBear }, + { 55, EntityType.Tnt }, + { 56, EntityType.Rabbit }, + { 57, EntityType.Salmon }, + { 58, EntityType.Sheep }, + { 59, EntityType.Shulker }, + { 60, EntityType.ShulkerBullet }, + { 61, EntityType.Silverfish }, + { 62, EntityType.Skeleton }, + { 63, EntityType.SkeletonHorse }, + { 64, EntityType.Slime }, + { 65, EntityType.SmallFireball }, + { 66, EntityType.SnowGolem }, + { 67, EntityType.Snowball }, + { 68, EntityType.SpectralArrow }, + { 69, EntityType.Spider }, + { 70, EntityType.Squid }, + { 71, EntityType.Stray }, + { 72, EntityType.TropicalFish }, + { 73, EntityType.Turtle }, + { 74, EntityType.Egg }, + { 75, EntityType.EnderPearl }, + { 76, EntityType.ExperienceBottle }, + { 77, EntityType.Potion }, + { 78, EntityType.Vex }, + { 79, EntityType.Villager }, + { 80, EntityType.IronGolem }, + { 81, EntityType.Vindicator }, + { 82, EntityType.Witch }, + { 83, EntityType.Wither }, + { 84, EntityType.WitherSkeleton }, + { 85, EntityType.WitherSkull }, + { 86, EntityType.Wolf }, + { 87, EntityType.Zombie }, + { 88, EntityType.ZombieHorse }, + { 89, EntityType.ZombieVillager }, + { 90, EntityType.Phantom }, + { 91, EntityType.LightningBolt }, + { 92, EntityType.Player }, + { 93, EntityType.FishingBobber }, + { 94, EntityType.Trident }, + }; + + protected override Dictionary GetDict() + { + return mappingsMobs; + } + + protected override Dictionary GetDictNonLiving() + { + return mappingsObjects; + } + } +} diff --git a/MinecraftClient/Mapping/EntityPalettes/EntityPalette114.cs b/MinecraftClient/Mapping/EntityPalettes/EntityPalette114.cs new file mode 100644 index 00000000..58ce763a --- /dev/null +++ b/MinecraftClient/Mapping/EntityPalettes/EntityPalette114.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections.Generic; + +namespace MinecraftClient.Mapping.EntityPalettes +{ + /// + /// Defines mappings for Minecraft 1.14. + /// Automatically generated using EntityPaletteGenerator.cs + /// + public class EntityPalette114 : EntityPalette + { + private static Dictionary mappings = new Dictionary(); + + static EntityPalette114() + { + mappings[0] = EntityType.AreaEffectCloud; + mappings[1] = EntityType.ArmorStand; + mappings[2] = EntityType.Arrow; + mappings[3] = EntityType.Bat; + mappings[4] = EntityType.Blaze; + mappings[5] = EntityType.Boat; + mappings[6] = EntityType.Cat; + mappings[7] = EntityType.CaveSpider; + mappings[8] = EntityType.Chicken; + mappings[9] = EntityType.Cod; + mappings[10] = EntityType.Cow; + mappings[11] = EntityType.Creeper; + mappings[12] = EntityType.Donkey; + mappings[13] = EntityType.Dolphin; + mappings[14] = EntityType.DragonFireball; + mappings[15] = EntityType.Drowned; + mappings[16] = EntityType.ElderGuardian; + mappings[17] = EntityType.EndCrystal; + mappings[18] = EntityType.EnderDragon; + mappings[19] = EntityType.Enderman; + mappings[20] = EntityType.Endermite; + mappings[21] = EntityType.EvokerFangs; + mappings[22] = EntityType.Evoker; + mappings[23] = EntityType.ExperienceOrb; + mappings[24] = EntityType.EyeOfEnder; + mappings[25] = EntityType.FallingBlock; + mappings[26] = EntityType.FireworkRocket; + mappings[27] = EntityType.Fox; + mappings[28] = EntityType.Ghast; + mappings[29] = EntityType.Giant; + mappings[30] = EntityType.Guardian; + mappings[31] = EntityType.Horse; + mappings[32] = EntityType.Husk; + mappings[33] = EntityType.Illusioner; + mappings[34] = EntityType.Item; + mappings[35] = EntityType.ItemFrame; + mappings[36] = EntityType.Fireball; + mappings[37] = EntityType.LeashKnot; + mappings[38] = EntityType.Llama; + mappings[39] = EntityType.LlamaSpit; + mappings[40] = EntityType.MagmaCube; + mappings[41] = EntityType.Minecart; + mappings[42] = EntityType.ChestMinecart; + mappings[43] = EntityType.CommandBlockMinecart; + mappings[44] = EntityType.FurnaceMinecart; + mappings[45] = EntityType.HopperMinecart; + mappings[46] = EntityType.SpawnerMinecart; + mappings[47] = EntityType.TntMinecart; + mappings[48] = EntityType.Mule; + mappings[49] = EntityType.Mooshroom; + mappings[50] = EntityType.Ocelot; + mappings[51] = EntityType.Painting; + mappings[52] = EntityType.Panda; + mappings[53] = EntityType.Parrot; + mappings[54] = EntityType.Pig; + mappings[55] = EntityType.Pufferfish; + mappings[56] = EntityType.ZombiePigman; + mappings[57] = EntityType.PolarBear; + mappings[58] = EntityType.Tnt; + mappings[59] = EntityType.Rabbit; + mappings[60] = EntityType.Salmon; + mappings[61] = EntityType.Sheep; + mappings[62] = EntityType.Shulker; + mappings[63] = EntityType.ShulkerBullet; + mappings[64] = EntityType.Silverfish; + mappings[65] = EntityType.Skeleton; + mappings[66] = EntityType.SkeletonHorse; + mappings[67] = EntityType.Slime; + mappings[68] = EntityType.SmallFireball; + mappings[69] = EntityType.SnowGolem; + mappings[70] = EntityType.Snowball; + mappings[71] = EntityType.SpectralArrow; + mappings[72] = EntityType.Spider; + mappings[73] = EntityType.Squid; + mappings[74] = EntityType.Stray; + mappings[75] = EntityType.TraderLlama; + mappings[76] = EntityType.TropicalFish; + mappings[77] = EntityType.Turtle; + mappings[78] = EntityType.Egg; + mappings[79] = EntityType.EnderPearl; + mappings[80] = EntityType.ExperienceBottle; + mappings[81] = EntityType.Potion; + mappings[82] = EntityType.Trident; + mappings[83] = EntityType.Vex; + mappings[84] = EntityType.Villager; + mappings[85] = EntityType.IronGolem; + mappings[86] = EntityType.Vindicator; + mappings[87] = EntityType.Pillager; + mappings[88] = EntityType.WanderingTrader; + mappings[89] = EntityType.Witch; + mappings[90] = EntityType.Wither; + mappings[91] = EntityType.WitherSkeleton; + mappings[92] = EntityType.WitherSkull; + mappings[93] = EntityType.Wolf; + mappings[94] = EntityType.Zombie; + mappings[95] = EntityType.ZombieHorse; + mappings[96] = EntityType.ZombieVillager; + mappings[97] = EntityType.Phantom; + mappings[98] = EntityType.Ravager; + mappings[99] = EntityType.LightningBolt; + mappings[100] = EntityType.Player; + mappings[101] = EntityType.FishingBobber; + } + + protected override Dictionary GetDict() + { + return mappings; + } + } +} diff --git a/MinecraftClient/Mapping/EntityPalettes/EntityPalette115.cs b/MinecraftClient/Mapping/EntityPalettes/EntityPalette115.cs new file mode 100644 index 00000000..d01955ef --- /dev/null +++ b/MinecraftClient/Mapping/EntityPalettes/EntityPalette115.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections.Generic; + +namespace MinecraftClient.Mapping.EntityPalettes +{ + /// + /// Defines mappings for Minecraft 1.15. + /// Automatically generated using EntityPaletteGenerator.cs + /// + public class EntityPalette115 : EntityPalette + { + private static Dictionary mappings = new Dictionary(); + + static EntityPalette115() + { + mappings[0] = EntityType.AreaEffectCloud; + mappings[1] = EntityType.ArmorStand; + mappings[2] = EntityType.Arrow; + mappings[3] = EntityType.Bat; + mappings[4] = EntityType.Bee; + mappings[5] = EntityType.Blaze; + mappings[6] = EntityType.Boat; + mappings[7] = EntityType.Cat; + mappings[8] = EntityType.CaveSpider; + mappings[9] = EntityType.Chicken; + mappings[10] = EntityType.Cod; + mappings[11] = EntityType.Cow; + mappings[12] = EntityType.Creeper; + mappings[13] = EntityType.Donkey; + mappings[14] = EntityType.Dolphin; + mappings[15] = EntityType.DragonFireball; + mappings[16] = EntityType.Drowned; + mappings[17] = EntityType.ElderGuardian; + mappings[18] = EntityType.EndCrystal; + mappings[19] = EntityType.EnderDragon; + mappings[20] = EntityType.Enderman; + mappings[21] = EntityType.Endermite; + mappings[22] = EntityType.EvokerFangs; + mappings[23] = EntityType.Evoker; + mappings[24] = EntityType.ExperienceOrb; + mappings[25] = EntityType.EyeOfEnder; + mappings[26] = EntityType.FallingBlock; + mappings[27] = EntityType.FireworkRocket; + mappings[28] = EntityType.Fox; + mappings[29] = EntityType.Ghast; + mappings[30] = EntityType.Giant; + mappings[31] = EntityType.Guardian; + mappings[32] = EntityType.Horse; + mappings[33] = EntityType.Husk; + mappings[34] = EntityType.Illusioner; + mappings[35] = EntityType.Item; + mappings[36] = EntityType.ItemFrame; + mappings[37] = EntityType.Fireball; + mappings[38] = EntityType.LeashKnot; + mappings[39] = EntityType.Llama; + mappings[40] = EntityType.LlamaSpit; + mappings[41] = EntityType.MagmaCube; + mappings[42] = EntityType.Minecart; + mappings[43] = EntityType.ChestMinecart; + mappings[44] = EntityType.CommandBlockMinecart; + mappings[45] = EntityType.FurnaceMinecart; + mappings[46] = EntityType.HopperMinecart; + mappings[47] = EntityType.SpawnerMinecart; + mappings[48] = EntityType.TntMinecart; + mappings[49] = EntityType.Mule; + mappings[50] = EntityType.Mooshroom; + mappings[51] = EntityType.Ocelot; + mappings[52] = EntityType.Painting; + mappings[53] = EntityType.Panda; + mappings[54] = EntityType.Parrot; + mappings[55] = EntityType.Pig; + mappings[56] = EntityType.Pufferfish; + mappings[57] = EntityType.ZombiePigman; + mappings[58] = EntityType.PolarBear; + mappings[59] = EntityType.Tnt; + mappings[60] = EntityType.Rabbit; + mappings[61] = EntityType.Salmon; + mappings[62] = EntityType.Sheep; + mappings[63] = EntityType.Shulker; + mappings[64] = EntityType.ShulkerBullet; + mappings[65] = EntityType.Silverfish; + mappings[66] = EntityType.Skeleton; + mappings[67] = EntityType.SkeletonHorse; + mappings[68] = EntityType.Slime; + mappings[69] = EntityType.SmallFireball; + mappings[70] = EntityType.SnowGolem; + mappings[71] = EntityType.Snowball; + mappings[72] = EntityType.SpectralArrow; + mappings[73] = EntityType.Spider; + mappings[74] = EntityType.Squid; + mappings[75] = EntityType.Stray; + mappings[76] = EntityType.TraderLlama; + mappings[77] = EntityType.TropicalFish; + mappings[78] = EntityType.Turtle; + mappings[79] = EntityType.Egg; + mappings[80] = EntityType.EnderPearl; + mappings[81] = EntityType.ExperienceBottle; + mappings[82] = EntityType.Potion; + mappings[83] = EntityType.Trident; + mappings[84] = EntityType.Vex; + mappings[85] = EntityType.Villager; + mappings[86] = EntityType.IronGolem; + mappings[87] = EntityType.Vindicator; + mappings[88] = EntityType.Pillager; + mappings[89] = EntityType.WanderingTrader; + mappings[90] = EntityType.Witch; + mappings[91] = EntityType.Wither; + mappings[92] = EntityType.WitherSkeleton; + mappings[93] = EntityType.WitherSkull; + mappings[94] = EntityType.Wolf; + mappings[95] = EntityType.Zombie; + mappings[96] = EntityType.ZombieHorse; + mappings[97] = EntityType.ZombieVillager; + mappings[98] = EntityType.Phantom; + mappings[99] = EntityType.Ravager; + mappings[100] = EntityType.LightningBolt; + mappings[101] = EntityType.Player; + mappings[102] = EntityType.FishingBobber; + } + + protected override Dictionary GetDict() + { + return mappings; + } + } +} diff --git a/MinecraftClient/Mapping/EntityPalettes/EntityPaletteGenerator.cs b/MinecraftClient/Mapping/EntityPalettes/EntityPaletteGenerator.cs new file mode 100644 index 00000000..141bcf6d --- /dev/null +++ b/MinecraftClient/Mapping/EntityPalettes/EntityPaletteGenerator.cs @@ -0,0 +1,20 @@ +using MinecraftClient.Protocol; + +namespace MinecraftClient.Mapping.EntityPalettes +{ + /// + /// Generator for MCC ItemType enumeration + /// + public static class EntityPaletteGenerator + { + /// + /// Generate EntityType.cs from Minecraft registries.json + /// + /// path to registries.json + /// java -cp minecraft_server.jar net.minecraft.data.Main --reports + public static void GenerateEntityTypes(string registriesJsonFile) + { + DataTypeGenerator.GenerateEnumWithPalette(registriesJsonFile, "minecraft:entity_type", "EntityType", "MinecraftClient.Mapping", "EntityPalette", "MinecraftClient.Mapping.EntityPalettes"); + } + } +} diff --git a/MinecraftClient/Mapping/EntityPalettes/EntityType114.cs b/MinecraftClient/Mapping/EntityPalettes/EntityType114.cs new file mode 100644 index 00000000..52ff21de --- /dev/null +++ b/MinecraftClient/Mapping/EntityPalettes/EntityType114.cs @@ -0,0 +1,108 @@ +namespace MinecraftClient.Mapping +{ + public enum EntityType115 + { + AreaEffectCloud = 0, + ArmorStand = 1, + Arrow = 2, + Bat = 3, + Blaze = 4, + Boat = 5, + Cat = 6, + CaveSpider = 7, + Chicken = 8, + Cod = 9, + Cow = 10, + Creeper = 11, + Donkey = 12, + Dolphin = 13, + DragonFireball = 14, + Drowned = 15, + ElderGuardian = 16, + EndCrystal = 17, + EnderDragon = 18, + Enderman = 19, + Endermite = 20, + EvokerFangs = 21, + Evoker = 22, + ExperienceOrb = 23, + EyeOfEnder = 24, + FallingBlock = 25, + FireworkRocket = 26, + Fox = 27, + Ghast = 28, + Giant = 29, + Guardian = 30, + Horse = 31, + Husk = 32, + Illusioner = 33, + Item = 34, + ItemFrame = 35, + Fireball = 36, + LeashKnot = 37, + Llama = 38, + LlamaSpit = 39, + MagmaCube = 40, + Minecart = 41, + ChestMinecart = 42, + CommandBlockMinecart = 43, + FurnaceMinecart = 44, + HopperMinecart = 45, + SpawnerMinecart = 46, + TntMinecart = 47, + Mule = 48, + Mooshroom = 49, + Ocelot = 50, + Painting = 51, + Panda = 52, + Parrot = 53, + Pig = 54, + Pufferfish = 55, + ZombiePigman = 56, + PolarBear = 57, + Tnt = 58, + Rabbit = 59, + Salmon = 60, + Sheep = 61, + Shulker = 62, + ShulkerBullet = 63, + Silverfish = 64, + Skeleton = 65, + SkeletonHorse = 66, + Slime = 67, + SmallFireball = 68, + SnowGolem = 69, + Snowball = 70, + SpectralArrow = 71, + Spider = 72, + Squid = 73, + Stray = 74, + TraderLlama = 75, + TropicalFish = 76, + Turtle = 77, + Egg = 78, + EnderPearl = 79, + ExperienceBottle = 80, + Potion = 81, + Trident = 82, + Vex = 83, + Villager = 84, + IronGolem = 85, + Vindicator = 86, + Pillager = 87, + WanderingTrader = 88, + Witch = 89, + Wither = 90, + WitherSkeleton = 91, + WitherSkull = 92, + Wolf = 93, + Zombie = 94, + ZombieHorse = 95, + ZombieVillager = 96, + Phantom = 97, + Ravager = 98, + LightningBolt = 99, + Player = 100, + FishingBobber = 101, + } +} diff --git a/MinecraftClient/Mapping/EntityPalettes/EntityType115.cs b/MinecraftClient/Mapping/EntityPalettes/EntityType115.cs new file mode 100644 index 00000000..66361ac0 --- /dev/null +++ b/MinecraftClient/Mapping/EntityPalettes/EntityType115.cs @@ -0,0 +1,109 @@ +namespace MinecraftClient.Mapping +{ + public enum EntityType114 + { + AreaEffectCloud = 0, + ArmorStand = 1, + Arrow = 2, + Bat = 3, + Bee = 4, + Blaze = 5, + Boat = 6, + Cat = 7, + CaveSpider = 8, + Chicken = 9, + Cod = 10, + Cow = 11, + Creeper = 12, + Donkey = 13, + Dolphin = 14, + DragonFireball = 15, + Drowned = 16, + ElderGuardian = 17, + EndCrystal = 18, + EnderDragon = 19, + Enderman = 20, + Endermite = 21, + EvokerFangs = 22, + Evoker = 23, + ExperienceOrb = 24, + EyeOfEnder = 25, + FallingBlock = 26, + FireworkRocket = 27, + Fox = 28, + Ghast = 29, + Giant = 30, + Guardian = 31, + Horse = 32, + Husk = 33, + Illusioner = 34, + Item = 35, + ItemFrame = 36, + Fireball = 37, + LeashKnot = 38, + Llama = 39, + LlamaSpit = 40, + MagmaCube = 41, + Minecart = 42, + ChestMinecart = 43, + CommandBlockMinecart = 44, + FurnaceMinecart = 45, + HopperMinecart = 46, + SpawnerMinecart = 47, + TntMinecart = 48, + Mule = 49, + Mooshroom = 50, + Ocelot = 51, + Painting = 52, + Panda = 53, + Parrot = 54, + Pig = 55, + Pufferfish = 56, + ZombiePigman = 57, + PolarBear = 58, + Tnt = 59, + Rabbit = 60, + Salmon = 61, + Sheep = 62, + Shulker = 63, + ShulkerBullet = 64, + Silverfish = 65, + Skeleton = 66, + SkeletonHorse = 67, + Slime = 68, + SmallFireball = 69, + SnowGolem = 70, + Snowball = 71, + SpectralArrow = 72, + Spider = 73, + Squid = 74, + Stray = 75, + TraderLlama = 76, + TropicalFish = 77, + Turtle = 78, + Egg = 79, + EnderPearl = 80, + ExperienceBottle = 81, + Potion = 82, + Trident = 83, + Vex = 84, + Villager = 85, + IronGolem = 86, + Vindicator = 87, + Pillager = 88, + WanderingTrader = 89, + Witch = 90, + Wither = 91, + WitherSkeleton = 92, + WitherSkull = 93, + Wolf = 94, + Zombie = 95, + ZombieHorse = 96, + ZombieVillager = 97, + Phantom = 98, + Ravager = 99, + LightningBolt = 100, + Player = 101, + FishingBobber = 102, + } +} diff --git a/MinecraftClient/Mapping/EntityType.cs b/MinecraftClient/Mapping/EntityType.cs index 9333f222..63ae6de2 100644 --- a/MinecraftClient/Mapping/EntityType.cs +++ b/MinecraftClient/Mapping/EntityType.cs @@ -1,14 +1,121 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - namespace MinecraftClient.Mapping { + /// + /// Represents Minecraft Entity Types + /// + /// + /// Generated from registries.json using EntityPaletteGenerator.cs. + /// Typical steps to handle new entity IDs for newer Minecraft versions: + /// 1. Generate registries.json using data reporting on Vanilla Minecraft (https://wiki.vg/Data_Generators) + /// 2. Generate temporary EntityTypeXXX.cs and EntityPaletteXXX.cs using EntityPaletteGenerator.cs + /// 3. Perform a diff with existing versions, add missing entries in EntityType.cs and EntityTypeExtensions.cs + /// 4. If existing entity IDs were not randomized by Mojang, simply add missing state entries to the latest existing PaletteXXX.cs + /// 5. If existing entity IDs were randomized, add a new palette as PaletteXXX.cs into the codebase (worst case) + /// public enum EntityType { - MobAndAnimal, + AreaEffectCloud, + ArmorStand, + Arrow, + Bat, + Bee, + Blaze, + Boat, + Cat, + CaveSpider, + Chicken, + Cod, + Cow, + Creeper, + Donkey, + Dolphin, + DragonFireball, + Drowned, + ElderGuardian, + EndCrystal, + EnderDragon, + Enderman, + Endermite, + EvokerFangs, + Evoker, + ExperienceOrb, + EyeOfEnder, + FallingBlock, + FireworkRocket, + Fox, + Ghast, + Giant, + Guardian, + Horse, + Husk, + Illusioner, + Item, + ItemFrame, + Fireball, + LeashKnot, + Llama, + LlamaSpit, + MagmaCube, + Minecart, + ChestMinecart, + CommandBlockMinecart, + FurnaceMinecart, + HopperMinecart, + SpawnerMinecart, + TntMinecart, + Mule, + Mooshroom, + Ocelot, + Painting, + Panda, + Parrot, + Pig, + Pufferfish, + ZombiePigman, + PolarBear, + Tnt, + Rabbit, + Salmon, + Sheep, + Shulker, + ShulkerBullet, + Silverfish, + Skeleton, + SkeletonHorse, + Slime, + SmallFireball, + SnowGolem, + Snowball, + SpectralArrow, + Spider, + Squid, + Stray, + TraderLlama, + TropicalFish, + Turtle, + Egg, + EnderPearl, + ExperienceBottle, + Potion, + Trident, + Vex, + Villager, + IronGolem, + Vindicator, + Pillager, + WanderingTrader, + Witch, + Wither, + WitherSkeleton, + WitherSkull, + Wolf, + Zombie, + ZombieHorse, + ZombieVillager, + Phantom, + Ravager, + LightningBolt, Player, - NonLivingThings, + FishingBobber, } } diff --git a/MinecraftClient/Mapping/EntityTypeExtensions.cs b/MinecraftClient/Mapping/EntityTypeExtensions.cs new file mode 100644 index 00000000..93dc596a --- /dev/null +++ b/MinecraftClient/Mapping/EntityTypeExtensions.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MinecraftClient.Mapping +{ + public static class EntityTypeExtensions + { + /// + /// Return TRUE if the Entity is an hostile mob + /// + /// New mobs added in newer Minecraft versions might be absent from the list + /// TRUE if hostile + public static bool IsHostile(this EntityType e) + { + switch (e) + { + case EntityType.Blaze: + case EntityType.Creeper: + case EntityType.Drowned: + case EntityType.Evoker: + case EntityType.Ghast: + case EntityType.Guardian: + case EntityType.Husk: + case EntityType.MagmaCube: + case EntityType.Phantom: + case EntityType.Pillager: + case EntityType.Ravager: + case EntityType.Shulker: + case EntityType.Silverfish: + case EntityType.Skeleton: + case EntityType.Slime: + case EntityType.Stray: + case EntityType.Vex: + case EntityType.Vindicator: + case EntityType.Witch: + case EntityType.WitherSkeleton: + case EntityType.Zombie: + case EntityType.ZombiePigman: + case EntityType.ZombieVillager: + return true; + default: + return false; + } + } + } +} diff --git a/MinecraftClient/McTcpClient.cs b/MinecraftClient/McTcpClient.cs index c5fb4027..734e78df 100644 --- a/MinecraftClient/McTcpClient.cs +++ b/MinecraftClient/McTcpClient.cs @@ -1222,47 +1222,16 @@ namespace MinecraftClient } /// - /// Called when a non-living entity spawned (fishing hook, minecart, etc) + /// Called when an entity spawned /// - /// - /// - /// - /// - public void OnSpawnEntity(int EntityID, int TypeID, Guid UUID, Location location) + public void OnSpawnEntity(Entity entity) { - if (entities.ContainsKey(EntityID)) return; - Entity entity = new Entity(EntityID, TypeID, EntityType.NonLivingThings, location, UUID); - entities.Add(EntityID, entity); - foreach (ChatBot bot in bots.ToArray()) - { - try - { - bot.OnEntitySpawn(entity); - } - catch (Exception e) - { - if (!(e is ThreadAbortException)) - { - ConsoleIO.WriteLogLine("OnEntitySpawn: Got error from " + bot.ToString() + ": " + e.ToString()); - } - else throw; //ThreadAbortException should not be caught - } - } - } + // The entity should not already exist, but if it does, let's consider the previous one is being destroyed + if (entities.ContainsKey(entity.ID)) + OnDestroyEntities(new[] { entity.ID }); + + entities.Add(entity.ID, entity); - /// - /// Called when an Entity was created/spawned. - /// - /// - /// - /// - /// - /// Cannot determine is a Mob or a Cuty Animal - public void OnSpawnLivingEntity(int EntityID, int TypeID, Guid UUID, Location location) - { - if (entities.ContainsKey(EntityID)) return; - Entity entity = new Entity(EntityID, TypeID, EntityType.MobAndAnimal, location, UUID); - entities.Add(EntityID, entity); foreach (ChatBot bot in bots.ToArray()) { try diff --git a/MinecraftClient/MinecraftClient.csproj b/MinecraftClient/MinecraftClient.csproj index a62ed363..f549f177 100644 --- a/MinecraftClient/MinecraftClient.csproj +++ b/MinecraftClient/MinecraftClient.csproj @@ -117,9 +117,15 @@ - + + + + + + + @@ -129,6 +135,7 @@ + diff --git a/MinecraftClient/Program.cs b/MinecraftClient/Program.cs index 462cbfcb..b5a4f7e7 100644 --- a/MinecraftClient/Program.cs +++ b/MinecraftClient/Program.cs @@ -20,6 +20,7 @@ namespace MinecraftClient /// Typical steps to update MCC for a new Minecraft version /// - Implement protocol changes (see Protocol18.cs) /// - Handle new block types and states (see Material.cs) + /// - Add support for new entity types (see EntityType.cs) /// - Add new item types for inventories (see ItemType.cs) /// - Mark new version as handled (see ProtocolHandler.cs) /// - Update MCHighestVersion field below (for versionning) diff --git a/MinecraftClient/Protocol/DataTypeGenerator.cs b/MinecraftClient/Protocol/DataTypeGenerator.cs new file mode 100644 index 00000000..f0070523 --- /dev/null +++ b/MinecraftClient/Protocol/DataTypeGenerator.cs @@ -0,0 +1,144 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.IO; + +namespace MinecraftClient.Protocol +{ + /// + /// Generic generator for MCC Enumerations such as ItemType or EntityType, mapping protocol IDs to actual enumeration fields such as 1 => Stone for inventories. + /// Works by processing Minecraft registries.json exported from minecraft_server.jar + /// + /// java -cp minecraft_server.jar net.minecraft.data.Main --reports + public static class DataTypeGenerator + { + /// + /// Read Minecraft registry from Json and build a dictionary + /// + /// Path to registries.json generated from Minecraft server Jar + /// Name of registry we want to process, e.g. minecraft:item + /// + private static Dictionary LoadRegistry(string registriesJsonFile, string jsonRegistryName) + { + Json.JSONData rawJson = Json.ParseJson(File.ReadAllText(registriesJsonFile)); + Json.JSONData rawRegistry = rawJson.Properties[jsonRegistryName].Properties["entries"]; + Dictionary registry = new Dictionary(); + + foreach (KeyValuePair entry in rawRegistry.Properties) + { + int entryId = int.Parse(entry.Value.Properties["protocol_id"].StringValue); + + //minecraft:item_name => ItemName + string entryName = String.Concat( + entry.Key.Replace("minecraft:", "") + .Split('_') + .Select(word => char.ToUpper(word[0]) + word.Substring(1)) + ); + + if (registry.ContainsKey(entryId)) + throw new InvalidDataException("Duplicate entry ID " + entryId + "!?"); + + registry.Add(entryId, entryName); + } + + return registry; + } + + /// + /// Generate MCC Enum from a Minecraft registry without Palette (static enum that does not change between versions) + /// + /// Path to registries.json generated from Minecraft server Jar + /// Name of registry we want to process, e.g. minecraft:item + /// Output enum name, e.g. ItemType (output file will be ItemType.cs) + /// Output enum namespace, e.g. MinecraftClient.Inventory + public static void GenerateEnum(string registriesJsonFile, string jsonRegistryName, string outputEnum, string enumNamespace) + { + List outputEnumLines = new List(); + + outputEnumLines.AddRange(new[] { + "namespace " + enumNamespace, + "{", + " public enum " + outputEnum, + " {" + }); + + Dictionary registry = LoadRegistry(registriesJsonFile, jsonRegistryName); + foreach (KeyValuePair entry in registry) + outputEnumLines.Add(" " + entry.Value + " = " + entry.Key + ','); + + outputEnumLines.AddRange(new[] { + " }", + "}" + }); + + string outputEnumPath = Path.Combine(Path.GetDirectoryName(registriesJsonFile), outputEnum + "XXX.cs"); + File.WriteAllLines(outputEnumPath, outputEnumLines); + } + + /// + /// Generate MCC Enum from a Minecraft registry with Palette (dynamic enum that changes between versions) + /// + /// Path to registries.json generated from Minecraft server Jar + /// Name of registry we want to process, e.g. minecraft:item + /// Output enum name, e.g. ItemType (output file will be ItemType.cs) + /// Enum namespace, e.g. MinecraftClient.Mapping + /// Output palette name, e.g. ItemPalette (output file will be ItemPalette.cs and ItemPaletteXXX.cs) + /// Palette namespace, e.g. MinecraftClient.EntityPalettes + public static void GenerateEnumWithPalette(string registriesJsonFile, string jsonRegistryName, string outputEnum, string enumNamespace, string outputPalette, string paletteNamespace) + { + List outputEnumLines = new List(); + List outputPaletteLines = new List(); + + outputEnumLines.AddRange(new[] { + "namespace " + enumNamespace, + "{", + " public enum " + outputEnum, + " {" + }); + + outputPaletteLines.AddRange(new[] { + "using System;", + "using System.Collections.Generic;", + "", + "namespace " + paletteNamespace, + "{", + " public class " + outputPalette + "XXX : " + outputPalette, + " {", + " private static Dictionary mappings = new Dictionary();", + "", + " static " + outputPalette + "XXX()", + " {", + }); + + Dictionary registry = LoadRegistry(registriesJsonFile, jsonRegistryName); + + foreach (KeyValuePair entry in registry) + { + outputEnumLines.Add(" " + entry.Value + ','); + outputPaletteLines.Add(" mappings[" + entry.Key + "] = " + outputEnum + "." + entry.Value + ";"); + } + + outputEnumLines.AddRange(new[] { + " }", + "}" + }); + + outputPaletteLines.AddRange(new[] { + " }", + "", + " protected override Dictionary GetDict()", + " {", + " return mappings;", + " }", + " }", + "}" + }); + + string outputEnumPath = Path.Combine(Path.GetDirectoryName(registriesJsonFile), outputEnum + "XXX.cs"); + string outputPalettePath = Path.Combine(Path.GetDirectoryName(registriesJsonFile), outputPalette + "XXX.cs"); + + File.WriteAllLines(outputEnumPath, outputEnumLines); + File.WriteAllLines(outputPalettePath, outputPaletteLines); + } + } +} diff --git a/MinecraftClient/Protocol/Handlers/DataTypes.cs b/MinecraftClient/Protocol/Handlers/DataTypes.cs index 6cccbdd9..bad87456 100644 --- a/MinecraftClient/Protocol/Handlers/DataTypes.cs +++ b/MinecraftClient/Protocol/Handlers/DataTypes.cs @@ -6,6 +6,7 @@ using System.Net.Sockets; using MinecraftClient.Mapping; using MinecraftClient.Crypto; using MinecraftClient.Inventory; +using MinecraftClient.Mapping.EntityPalettes; namespace MinecraftClient.Protocol.Handlers { @@ -321,9 +322,8 @@ namespace MinecraftClient.Protocol.Handlers } /// - /// Read a single item slot from a cache of byte and remove it from the cache + /// Read a single item slot from a cache of bytes and remove it from the cache /// - /// Item /// The item that was read or NULL for an empty slot public Item ReadNextItemSlot(Queue cache) { @@ -354,6 +354,45 @@ namespace MinecraftClient.Protocol.Handlers } } + /// + /// Read entity information from a cache of bytes and remove it from the cache + /// + /// Mappings for converting entity type Ids to EntityType + /// TRUE for living entities (layout differs) + /// Entity information + public Entity ReadNextEntity(Queue cache, EntityPalette entityPalette, bool living) + { + int entityID = ReadNextVarInt(cache); + Guid entityUUID = Guid.Empty; + + if (protocolversion > Protocol18Handler.MC18Version) + { + entityUUID = ReadNextUUID(cache); + } + + EntityType entityType = entityPalette.FromId(ReadNextVarInt(cache), living); + Double entityX = ReadNextDouble(cache); + Double entityY = ReadNextDouble(cache); + Double entityZ = ReadNextDouble(cache); + byte entityYaw = ReadNextByte(cache); + byte entityPitch = ReadNextByte(cache); + + if (living) + { + byte entityHeadPitch = ReadNextByte(cache); + } + else + { + int metadata = ReadNextInt(cache); + } + + short velocityX = ReadNextShort(cache); + short velocityY = ReadNextShort(cache); + short velocityZ = ReadNextShort(cache); + + return new Entity(entityID, entityType, new Location(entityX, entityY, entityZ)); + } + /// /// Read an uncompressed Named Binary Tag blob and remove it from the cache (internal) /// diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index df8639f0..2c1cef89 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -9,6 +9,7 @@ using MinecraftClient.Proxy; using System.Security.Cryptography; using MinecraftClient.Mapping; using MinecraftClient.Mapping.BlockPalettes; +using MinecraftClient.Mapping.EntityPalettes; using MinecraftClient.Protocol.Handlers.Forge; using MinecraftClient.Inventory; @@ -50,6 +51,7 @@ namespace MinecraftClient.Protocol.Handlers Protocol18Forge pForge; Protocol18Terrain pTerrain; IMinecraftComHandler handler; + EntityPalette entityPalette; SocketWrapper socketWrapper; DataTypes dataTypes; Thread netRead; @@ -94,6 +96,16 @@ namespace MinecraftClient.Protocol.Handlers else Block.Palette = new Palette113(); } else Block.Palette = new Palette112(); + + if (protocolversion >= MC114Version) + { + if (protocolversion > MC1152Version && handler.GetEntityHandlingEnabled()) + throw new NotImplementedException("Please update entity types handling for this Minecraft version. See EntityType.cs"); + if (protocolversion >= MC115Version) + entityPalette = new EntityPalette115(); + else entityPalette = new EntityPalette114(); + } + else entityPalette = new EntityPalette113(); } /// @@ -195,7 +207,7 @@ namespace MinecraftClient.Protocol.Handlers } } // Regular in-game packets - switch (Protocol18PacketTypes.GetPacketIncomingType(packetID, protocolversion)) + else switch (Protocol18PacketTypes.GetPacketIncomingType(packetID, protocolversion)) { case PacketIncomingType.KeepAlive: SendPacket(PacketOutgoingType.KeepAlive, packetData); @@ -569,54 +581,18 @@ namespace MinecraftClient.Protocol.Handlers case PacketIncomingType.SpawnEntity: if (handler.GetEntityHandlingEnabled()) { - int EntityID = dataTypes.ReadNextVarInt(packetData); - Guid EntityUUID = Guid.Empty; - if (protocolversion > MC18Version) - { - EntityUUID = dataTypes.ReadNextUUID(packetData); - } - int EntityType = dataTypes.ReadNextVarInt(packetData); - Double X = dataTypes.ReadNextDouble(packetData); - Double Y = dataTypes.ReadNextDouble(packetData); - Double Z = dataTypes.ReadNextDouble(packetData); - byte EntityYaw = dataTypes.ReadNextByte(packetData); - byte EntityPitch = dataTypes.ReadNextByte(packetData); - int Data = dataTypes.ReadNextInt(packetData); - short VelocityX = dataTypes.ReadNextShort(packetData); - short VelocityY = dataTypes.ReadNextShort(packetData); - short VelocityZ = dataTypes.ReadNextShort(packetData); - - Location EntityLocation = new Location(X, Y, Z); - - handler.OnSpawnEntity(EntityID, EntityType, EntityUUID, EntityLocation); + Entity entity = dataTypes.ReadNextEntity(packetData, entityPalette, false); + handler.OnSpawnEntity(entity); } break; case PacketIncomingType.SpawnLivingEntity: - if (login_phase) break; // same packet ID with login packet if (handler.GetEntityHandlingEnabled()) { - int EntityID = dataTypes.ReadNextVarInt(packetData); - Guid EntityUUID = Guid.Empty; - if (protocolversion > MC18Version) - { - EntityUUID = dataTypes.ReadNextUUID(packetData); - } - int EntityType = dataTypes.ReadNextVarInt(packetData); - Double X = dataTypes.ReadNextDouble(packetData); - Double Y = dataTypes.ReadNextDouble(packetData); - Double Z = dataTypes.ReadNextDouble(packetData); - byte EntityYaw = dataTypes.ReadNextByte(packetData); - byte EntityPitch = dataTypes.ReadNextByte(packetData); - byte EntityHeadPitch = dataTypes.ReadNextByte(packetData); - short VelocityX = dataTypes.ReadNextShort(packetData); - short VelocityY = dataTypes.ReadNextShort(packetData); - short VelocityZ = dataTypes.ReadNextShort(packetData); - + Entity entity = dataTypes.ReadNextEntity(packetData, entityPalette, true); // packet before 1.15 has metadata at the end - - Location EntityLocation = new Location(X, Y, Z); - - handler.OnSpawnLivingEntity(EntityID, EntityType, EntityUUID, EntityLocation); + // this is not handled in dataTypes.ReadNextEntity() + // we are simply ignoring leftover data in packet + handler.OnSpawnEntity(entity); } break; case PacketIncomingType.SpawnPlayer: @@ -713,7 +689,6 @@ namespace MinecraftClient.Protocol.Handlers } break; case PacketIncomingType.TimeUpdate: - if (login_phase) break; long WorldAge = dataTypes.ReadNextLong(packetData); long TimeOfday = dataTypes.ReadNextLong(packetData); handler.OnTimeUpdate(WorldAge, TimeOfday); diff --git a/MinecraftClient/Protocol/Handlers/Protocol18PacketTypes.cs b/MinecraftClient/Protocol/Handlers/Protocol18PacketTypes.cs index b85a36bb..eef0045d 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18PacketTypes.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18PacketTypes.cs @@ -46,8 +46,8 @@ namespace MinecraftClient.Protocol.Handlers // Set Cooldown does not exists case 0x03: return PacketIncomingType.TimeUpdate; // Entity handling - case 0x0E: return PacketIncomingType.SpawnEntity; // for non-living entity - case 0x0F: return PacketIncomingType.SpawnLivingEntity; // for living entity + case 0x0E: return PacketIncomingType.SpawnEntity; + case 0x0F: return PacketIncomingType.SpawnLivingEntity; case 0x05: return PacketIncomingType.SpawnPlayer; case 0x13: return PacketIncomingType.DestroyEntities; case 0x15: return PacketIncomingType.EntityPosition; @@ -85,8 +85,8 @@ namespace MinecraftClient.Protocol.Handlers case 0x17: return PacketIncomingType.SetCooldown; case 0x44: return PacketIncomingType.TimeUpdate; // Entity handling - case 0x00: return PacketIncomingType.SpawnEntity; // for non-living entity - case 0x03: return PacketIncomingType.SpawnLivingEntity; // for living entity + case 0x00: return PacketIncomingType.SpawnEntity; + case 0x03: return PacketIncomingType.SpawnLivingEntity; case 0x05: return PacketIncomingType.SpawnPlayer; case 0x30: return PacketIncomingType.DestroyEntities; case 0x25: return PacketIncomingType.EntityPosition; @@ -122,8 +122,8 @@ namespace MinecraftClient.Protocol.Handlers case 0x17: return PacketIncomingType.SetCooldown; case 0x46: return PacketIncomingType.TimeUpdate; // Entity handling - case 0x00: return PacketIncomingType.SpawnEntity; // for non-living entity - case 0x03: return PacketIncomingType.SpawnLivingEntity; // for living entity + case 0x00: return PacketIncomingType.SpawnEntity; + case 0x03: return PacketIncomingType.SpawnLivingEntity; case 0x05: return PacketIncomingType.SpawnPlayer; case 0x31: return PacketIncomingType.DestroyEntities; case 0x26: return PacketIncomingType.EntityPosition; @@ -159,8 +159,8 @@ namespace MinecraftClient.Protocol.Handlers case 0x17: return PacketIncomingType.SetCooldown; case 0x47: return PacketIncomingType.TimeUpdate; // Entity handling - case 0x00: return PacketIncomingType.SpawnEntity; // for non-living entity - case 0x03: return PacketIncomingType.SpawnLivingEntity; // for living entity + case 0x00: return PacketIncomingType.SpawnEntity; + case 0x03: return PacketIncomingType.SpawnLivingEntity; case 0x05: return PacketIncomingType.SpawnPlayer; case 0x32: return PacketIncomingType.DestroyEntities; case 0x26: return PacketIncomingType.EntityPosition; @@ -196,8 +196,8 @@ namespace MinecraftClient.Protocol.Handlers case 0x18: return PacketIncomingType.SetCooldown; case 0x4A: return PacketIncomingType.TimeUpdate; // Entity handling - case 0x00: return PacketIncomingType.SpawnEntity; // for non-living entity - case 0x03: return PacketIncomingType.SpawnLivingEntity; // for living entity + case 0x00: return PacketIncomingType.SpawnEntity; + case 0x03: return PacketIncomingType.SpawnLivingEntity; case 0x05: return PacketIncomingType.SpawnPlayer; case 0x35: return PacketIncomingType.DestroyEntities; case 0x28: return PacketIncomingType.EntityPosition; @@ -232,8 +232,8 @@ namespace MinecraftClient.Protocol.Handlers case 0x17: return PacketIncomingType.SetCooldown; case 0x4E: return PacketIncomingType.TimeUpdate; // Entity handling - case 0x00: return PacketIncomingType.SpawnEntity; // for non-living entity - case 0x03: return PacketIncomingType.SpawnLivingEntity; // for living entity + case 0x00: return PacketIncomingType.SpawnEntity; + case 0x03: return PacketIncomingType.SpawnLivingEntity; case 0x05: return PacketIncomingType.SpawnPlayer; case 0x37: return PacketIncomingType.DestroyEntities; case 0x28: return PacketIncomingType.EntityPosition; @@ -268,8 +268,8 @@ namespace MinecraftClient.Protocol.Handlers case 0x18: return PacketIncomingType.SetCooldown; case 0x4F: return PacketIncomingType.TimeUpdate; // Entity handling - case 0x00: return PacketIncomingType.SpawnEntity; // for non-living entity - case 0x03: return PacketIncomingType.SpawnLivingEntity; // for living entity + case 0x00: return PacketIncomingType.SpawnEntity; + case 0x03: return PacketIncomingType.SpawnLivingEntity; case 0x05: return PacketIncomingType.SpawnPlayer; case 0x38: return PacketIncomingType.DestroyEntities; case 0x29: return PacketIncomingType.EntityPosition; diff --git a/MinecraftClient/Protocol/IMinecraftComHandler.cs b/MinecraftClient/Protocol/IMinecraftComHandler.cs index c870e5cd..778f5d4b 100644 --- a/MinecraftClient/Protocol/IMinecraftComHandler.cs +++ b/MinecraftClient/Protocol/IMinecraftComHandler.cs @@ -131,22 +131,10 @@ namespace MinecraftClient.Protocol void OnPluginChannelMessage(string channel, byte[] data); /// - /// Called when a non-living entity has spawned + /// Called when an entity has spawned /// - /// Entity ID - /// Entity Type ID - /// Entity UUID - /// Entity location - void OnSpawnEntity(int EntityID, int EntityType, Guid UUID, Location location); - - /// - /// Called when a living entity has spawned - /// - /// Entity ID - /// Entity Type ID - /// Entity UUID - /// Entity location - void OnSpawnLivingEntity(int EntityID, int EntityType, Guid UUID, Location location); + /// Spawned entity + void OnSpawnEntity(Entity entity); /// /// Called when a player has spawned @@ -156,7 +144,7 @@ namespace MinecraftClient.Protocol /// Entity location /// Player head yaw /// Player head pitch - void OnSpawnPlayer(int EntityID, Guid UUID, Location location, byte Yaw, byte Pitch); + void OnSpawnPlayer(int entityID, Guid uuid, Location location, byte yaw, byte pitch); /// /// Called when entities have despawned @@ -172,7 +160,7 @@ namespace MinecraftClient.Protocol /// Y offset /// Z offset /// TRUE if on ground - void OnEntityPosition(int EntityID, Double Dx, Double Dy, Double Dz,bool onGround); + void OnEntityPosition(int entityID, Double dx, Double dy, Double dz,bool onGround); /// /// Called when an entity moved to fixed coordinates @@ -182,28 +170,28 @@ namespace MinecraftClient.Protocol /// Y /// Z /// TRUE if on ground - void OnEntityTeleport(int EntityID, Double X, Double Y, Double Z, bool onGround); + void OnEntityTeleport(int entityID, Double x, Double y, Double z, bool onGround); /// /// Called when additional properties have been received for an entity /// /// Entity ID /// Dictionary of properties - void OnEntityProperties(int EntityID, Dictionary prop); + void OnEntityProperties(int entityID, Dictionary prop); /// /// Called when the world age has been updated /// /// World age /// Time of Day - void OnTimeUpdate(long WorldAge, long TimeOfDay); + void OnTimeUpdate(long worldAge, long timeOfDay); /// /// Called when inventory items have been received /// /// Inventory ID /// Item list - void OnWindowItems(byte inventoryID, Dictionary itemList); + void OnWindowItems(byte inventoryID, Dictionary itemList); /// /// Called when a single slot has been updated inside an inventory