Added enchanting

This commit is contained in:
Milutinke 2022-10-12 19:51:01 +02:00
parent d222d5f683
commit 4dc1b420f5
12 changed files with 474 additions and 1 deletions

View file

@ -0,0 +1,50 @@
using System.Collections.Generic;
using System.Globalization;
using MinecraftClient.Inventory;
namespace MinecraftClient.Commands
{
public class Enchant : Command
{
public override string CmdName { get { return "enchant"; } }
public override string CmdUsage { get { return "enchant <top|middle|bottom>"; } }
public override string CmdDesc { get { return "cmd.enchant.desc"; } }
public override string Run(McClient handler, string command, Dictionary<string, object>? localVars)
{
if (HasArg(command))
{
string slot = GetArg(command).ToLower().Trim();
int slotId = slot switch
{
"top" => 0,
"middle" => 1,
"bottom" => 2,
_ => -1
};
if (slotId == -1)
return Translations.TryGet("cmd.enchant.invalid_slot");
int containerId = -1;
foreach (var (id, container) in handler.GetInventories())
{
if (container.Type == ContainerType.Enchantment)
{
containerId = id;
break;
}
}
if (containerId == -1)
return Translations.TryGet("cmd.enchant.enchanting_table_not_opened");
return handler.ClickContainerButton(containerId, slotId) ? Translations.TryGet("cmd.enchant.clicked") : Translations.TryGet("cmd.enchant.not_clicked");
}
return GetCmdDescTranslated();
}
}
}

View file

@ -32,6 +32,13 @@ namespace MinecraftClient.Inventory
/// </summary>
public Dictionary<int, Item> Items;
/// <summary>
/// Container Properties
/// Used for Frunaces, Enchanting Table, Beacon, Brewing stand, Stone cutter, Loom and Lectern
/// More info about: https://wiki.vg/Protocol#Set_Container_Property
/// </summary>
public Dictionary<int, short> Properties;
/// <summary>
/// Create an empty container with ID, Type and Title
/// </summary>
@ -44,6 +51,7 @@ namespace MinecraftClient.Inventory
Type = type;
Title = title;
Items = new Dictionary<int, Item>();
Properties = new Dictionary<int, short>();
}
/// <summary>
@ -59,6 +67,7 @@ namespace MinecraftClient.Inventory
Type = type;
Title = title;
Items = items;
Properties = new Dictionary<int, short>();
}
/// <summary>
@ -73,6 +82,7 @@ namespace MinecraftClient.Inventory
Title = title;
Type = ConvertType.ToNew(type);
Items = new Dictionary<int, Item>();
Properties = new Dictionary<int, short>();
}
/// <summary>
@ -87,6 +97,7 @@ namespace MinecraftClient.Inventory
Type = GetContainerType(typeID);
Title = title;
Items = new Dictionary<int, Item>();
Properties = new Dictionary<int, short>();
}
/// <summary>
@ -99,6 +110,7 @@ namespace MinecraftClient.Inventory
Type = type;
Title = null;
Items = new Dictionary<int, Item>();
Properties = new Dictionary<int, short>();
}
/// <summary>
@ -112,6 +124,7 @@ namespace MinecraftClient.Inventory
Type = type;
Title = null;
Items = items;
Properties = new Dictionary<int, short>();
}
/// <summary>

View file

@ -0,0 +1,159 @@
using System;
using System.Collections.Generic;
using MinecraftClient.Protocol.Handlers;
namespace MinecraftClient.Inventory
{
public class EnchantmentMapping
{
// 1.14 - 1.15.2
private static Dictionary<short, Enchantment> enchantmentMappings114 = new Dictionary<short, Enchantment>()
{
//id type
{ 0, Enchantment.Protection },
{ 1, Enchantment.FireProtection },
{ 2, Enchantment.FeatherFalling },
{ 3, Enchantment.BlastProtection },
{ 4, Enchantment.ProjectileProtection },
{ 5, Enchantment.Respiration },
{ 6, Enchantment.AquaAffinity },
{ 7, Enchantment.Thorns },
{ 8, Enchantment.DepthStrieder },
{ 9, Enchantment.FrostWalker },
{ 10, Enchantment.BindingCurse },
{ 11, Enchantment.Sharpness },
{ 12, Enchantment.Smite },
{ 13, Enchantment.BaneOfArthropods },
{ 14, Enchantment.Knockback },
{ 15, Enchantment.FireAspect },
{ 16, Enchantment.Looting },
{ 17, Enchantment.Sweeping },
{ 18, Enchantment.Efficency },
{ 19, Enchantment.SilkTouch },
{ 20, Enchantment.Unbreaking },
{ 21, Enchantment.Fortune },
{ 22, Enchantment.Power },
{ 23, Enchantment.Punch },
{ 24, Enchantment.Flame },
{ 25, Enchantment.Infinity },
{ 26, Enchantment.LuckOfTheSea },
{ 27, Enchantment.Lure },
{ 28, Enchantment.Loyality },
{ 29, Enchantment.Impaling },
{ 30, Enchantment.Riptide },
{ 31, Enchantment.Channeling },
{ 32, Enchantment.Mending },
{ 33, Enchantment.VanishingCurse }
};
private static Dictionary<short, Enchantment> enchantmentMappings116Plus = new Dictionary<short, Enchantment>()
{
//id type
{ 0, Enchantment.Protection },
{ 1, Enchantment.FireProtection },
{ 2, Enchantment.FeatherFalling },
{ 3, Enchantment.BlastProtection },
{ 4, Enchantment.ProjectileProtection },
{ 5, Enchantment.Respiration },
{ 6, Enchantment.AquaAffinity },
{ 7, Enchantment.Thorns },
{ 8, Enchantment.DepthStrieder },
{ 9, Enchantment.FrostWalker },
{ 10, Enchantment.BindingCurse },
{ 11, Enchantment.SoulSpeed },
{ 12, Enchantment.Sharpness },
{ 13, Enchantment.Smite },
{ 14, Enchantment.BaneOfArthropods },
{ 15, Enchantment.Knockback },
{ 16, Enchantment.FireAspect },
{ 17, Enchantment.Looting },
{ 18, Enchantment.Sweeping },
{ 19, Enchantment.Efficency },
{ 20, Enchantment.SilkTouch },
{ 21, Enchantment.Unbreaking },
{ 22, Enchantment.Fortune },
{ 23, Enchantment.Power },
{ 24, Enchantment.Punch },
{ 25, Enchantment.Flame },
{ 26, Enchantment.Infinity },
{ 27, Enchantment.LuckOfTheSea },
{ 28, Enchantment.Lure },
{ 29, Enchantment.Loyality },
{ 30, Enchantment.Impaling },
{ 31, Enchantment.Riptide },
{ 32, Enchantment.Channeling },
{ 33, Enchantment.Multishot },
{ 34, Enchantment.QuickCharge },
{ 35, Enchantment.Piercing },
{ 36, Enchantment.Mending },
{ 37, Enchantment.VanishingCurse }
};
private static Dictionary<Enchantment, string> enchantmentNames = new Dictionary<Enchantment, string>()
{
//type
{ Enchantment.Protection, "Enchantment.Protection" } ,
{ Enchantment.FireProtection, "Enchantment.FireProtection" },
{ Enchantment.FeatherFalling, "Enchantment.FeatherFalling" },
{ Enchantment.BlastProtection, "Enchantment.BlastProtection" },
{ Enchantment.ProjectileProtection, "Enchantment.ProjectileProtection" },
{ Enchantment.Respiration, "Enchantment.Respiration" },
{ Enchantment.AquaAffinity, "Enchantment.AquaAffinity" },
{ Enchantment.Thorns, "Enchantment.Thorns" },
{ Enchantment.DepthStrieder, "Enchantment.DepthStrieder" },
{ Enchantment.FrostWalker, "Enchantment.FrostWalker" },
{ Enchantment.BindingCurse, "Enchantment.BindingCurse" },
{ Enchantment.SoulSpeed, "Enchantment.SoulSpeed" },
{ Enchantment.Sharpness, "Enchantment.Sharpness" },
{ Enchantment.Smite, "Enchantment.Smite" },
{ Enchantment.BaneOfArthropods, "Enchantment.BaneOfArthropods" },
{ Enchantment.Knockback, "Enchantment.Knockback" },
{ Enchantment.FireAspect, "Enchantment.FireAspect" },
{ Enchantment.Looting, "Enchantment.Looting" },
{ Enchantment.Sweeping, "Enchantment.Sweeping" },
{ Enchantment.Efficency, "Enchantment.Efficency" },
{ Enchantment.SilkTouch, "Enchantment.SilkTouch" },
{ Enchantment.Unbreaking, "Enchantment.Unbreaking" },
{ Enchantment.Fortune, "Enchantment.Fortune" },
{ Enchantment.Power, "Enchantment.Power" },
{ Enchantment.Punch, "Enchantment.Punch" },
{ Enchantment.Flame, "Enchantment.Flame" },
{ Enchantment.Infinity, "Enchantment.Infinity" },
{ Enchantment.LuckOfTheSea, "Enchantment.LuckOfTheSea" },
{ Enchantment.Lure, "Enchantment.Lure" },
{ Enchantment.Loyality, "Enchantment.Loyality" },
{ Enchantment.Impaling, "Enchantment.Impaling" },
{ Enchantment.Riptide, "Enchantment.Riptide" },
{ Enchantment.Channeling, "Enchantment.Channeling" },
{ Enchantment.Multishot, "Enchantment.Multishot" },
{ Enchantment.QuickCharge, "Enchantment.QuickCharge" },
{ Enchantment.Piercing, "Enchantment.Piercing" },
{ Enchantment.Mending, "Enchantment.Mending" },
{ Enchantment.VanishingCurse, "Enchantment.VanishingCurse" }
};
public static Enchantment GetEnchantmentById(int protocolVersion, short id)
{
if (protocolVersion < Protocol18Handler.MC_1_14_Version)
throw new Exception("Enchantments mappings are not implemented bellow 1.14");
Dictionary<short, Enchantment> map = enchantmentMappings116Plus;
if (protocolVersion >= Protocol18Handler.MC_1_14_Version && protocolVersion < Protocol18Handler.MC_1_16_Version)
map = enchantmentMappings114;
if (!map.ContainsKey(id))
throw new Exception("Got an Unknown Enchantment ID '" + id + "', please update the Mappings!");
return map[id];
}
public static string GetEnchantmentName(Enchantment enchantment)
{
if (!enchantmentNames.ContainsKey(enchantment))
return "Unknown Enchantment with ID: " + ((int)enchantment) + " (Probably not named in the code yet)";
return Translations.TryGet(enchantmentNames[enchantment]);
}
}
}

View file

@ -0,0 +1,23 @@
namespace MinecraftClient.Inventory
{
public enum EnchantmentPropertyInfo
{
// Levels that are required to enchant an item
TopEnchantmentLevelRequirement = 0,
MiddleEnchantmentLevelRequirement,
BottomEnchantmentLevelRequirement,
// Seed
EnchantmentSeed,
// Enchantment ids
TopEnchantmentId,
MiddleEnchantmentId,
BottomEnchantmentId,
// Shown on mouse hover over the top, middle and bottom slot
TopEnchantmentLevel,
MiddleEnchantmentLevel,
BottomEnchantmentLevel
}
}

View file

@ -0,0 +1,45 @@
namespace MinecraftClient.Inventory
{
// Not implemented for 1.14
public enum Enchantment : short
{
Protection = 0,
FireProtection,
FeatherFalling,
BlastProtection,
ProjectileProtection,
Respiration,
AquaAffinity,
Thorns,
DepthStrieder,
FrostWalker,
BindingCurse,
SoulSpeed,
Sharpness,
Smite,
BaneOfArthropods,
Knockback,
FireAspect,
Looting,
Sweeping,
Efficency,
SilkTouch,
Unbreaking,
Fortune,
Power,
Punch,
Flame,
Infinity,
LuckOfTheSea,
Lure,
Loyality,
Impaling,
Riptide,
Channeling,
Multishot,
QuickCharge,
Piercing,
Mending,
VanishingCurse
}
}

View file

@ -2650,6 +2650,64 @@ namespace MinecraftClient
}
}
/// <summary>
/// When received window properties from server.
/// Used for Frunaces, Enchanting Table, Beacon, Brewing stand, Stone cutter, Loom and Lectern
/// More info about: https://wiki.vg/Protocol#Set_Container_Property
/// </summary>
/// <param name="inventoryID">Inventory ID</param>
/// <param name="propertyId">Property ID</param>
/// <param name="propertyValue">Property Value</param>
public void OnWindowProperties(byte inventoryID, short propertyId, short propertyValue)
{
if (!inventories.ContainsKey(inventoryID))
return;
Container inventory = inventories[inventoryID];
//Log.Info("Got a property " + propertyId + " with value: " + propertyValue);
if (inventory.Properties.ContainsKey(propertyId))
inventory.Properties.Remove(propertyId);
inventory.Properties.Add(propertyId, propertyValue);
DispatchBotEvent(bot => bot.OnInventoryProperties(inventoryID, propertyId, propertyValue));
if (inventory.Type == ContainerType.Enchantment)
{
// We got the last property for enchantment
if ((int)propertyId == (int)EnchantmentPropertyInfo.BottomEnchantmentLevel && propertyValue != -1)
{
short topEnchantmentLevelRequirement = inventory.Properties[(int)EnchantmentPropertyInfo.TopEnchantmentLevelRequirement];
short middleEnchantmentLevelRequirement = inventory.Properties[(int)EnchantmentPropertyInfo.MiddleEnchantmentLevelRequirement];
short bottomEnchantmentLevelRequirement = inventory.Properties[(int)EnchantmentPropertyInfo.BottomEnchantmentLevelRequirement];
short enchantmentSeed = inventory.Properties[(int)EnchantmentPropertyInfo.EnchantmentSeed];
Enchantment topEnchantment = EnchantmentMapping.GetEnchantmentById(
GetProtocolVersion(),
inventory.Properties[(int)EnchantmentPropertyInfo.TopEnchantmentId]);
Enchantment middleEnchantment = EnchantmentMapping.GetEnchantmentById(
GetProtocolVersion(),
inventory.Properties[(int)EnchantmentPropertyInfo.MiddleEnchantmentId]);
Enchantment bottomEnchantment = EnchantmentMapping.GetEnchantmentById(
GetProtocolVersion(),
inventory.Properties[(int)EnchantmentPropertyInfo.BottomEnchantmentId]);
short topEnchantmentLevel = inventory.Properties[(int)EnchantmentPropertyInfo.TopEnchantmentId];
short middleEnchantmentLevel = inventory.Properties[(int)EnchantmentPropertyInfo.MiddleEnchantmentId];
short bottomEnchantmentLevel = inventory.Properties[(int)EnchantmentPropertyInfo.BottomEnchantmentId];
Log.Info(EnchantmentMapping.GetEnchantmentName(topEnchantment) + " " + topEnchantmentLevel + " > " + topEnchantmentLevelRequirement + " Levels");
Log.Info(EnchantmentMapping.GetEnchantmentName(middleEnchantment) + " " + middleEnchantmentLevel + " > " + middleEnchantmentLevelRequirement + " Levels");
Log.Info(EnchantmentMapping.GetEnchantmentName(bottomEnchantment) + " " + bottomEnchantmentLevel + " > " + bottomEnchantmentLevelRequirement + " Levels");
}
}
}
/// <summary>
/// When received window items from server.
/// </summary>
@ -3304,6 +3362,19 @@ namespace MinecraftClient
DispatchBotEvent(bot => bot.OnBlockChange(location, block));
}
/// <summary>
/// Send a click container button packet to the server.
/// Used for Enchanting table, Lectern, stone cutter and loom
/// </summary>
/// <param name="windowId">Id of the window being clicked</param>
/// <param name="buttonId">Id of the clicked button</param>
/// <returns>True if packet was successfully sent</returns>
public bool ClickContainerButton(int windowId, int buttonId)
{
return handler.ClickContainerButton(windowId, buttonId);
}
#endregion
}
}

View file

@ -770,6 +770,11 @@ namespace MinecraftClient.Protocol.Handlers
return false; //Currently not implemented
}
public bool ClickContainerButton(int windowId, int buttonId)
{
return false; //Currently not implemented
}
public bool SendCloseWindow(int windowId)
{
return false; //Currently not implemented

View file

@ -1364,6 +1364,13 @@ namespace MinecraftClient.Protocol.Handlers
handler.OnWindowItems(windowId, inventorySlots, stateId);
}
break;
case PacketTypesIn.WindowProperty:
byte containerId = dataTypes.ReadNextByte(packetData);
short propertyId = dataTypes.ReadNextShort(packetData);
short propertyValue = dataTypes.ReadNextShort(packetData);
handler.OnWindowProperties(containerId, propertyId, propertyValue);
break;
case PacketTypesIn.SetSlot:
if (handler.GetInventoryEnabled())
{
@ -2866,6 +2873,21 @@ namespace MinecraftClient.Protocol.Handlers
catch (ObjectDisposedException) { return false; }
}
public bool ClickContainerButton(int windowId, int buttonId)
{
try
{
List<byte> packet = new();
packet.Add((byte)windowId);
packet.Add((byte)buttonId);
SendPacket(PacketTypesOut.ClickWindowButton, packet);
return true;
}
catch (SocketException) { return false; }
catch (System.IO.IOException) { return false; }
catch (ObjectDisposedException) { return false; }
}
public bool SendAnimation(int animation, int playerid)
{
try

View file

@ -179,6 +179,16 @@ namespace MinecraftClient.Protocol
/// <returns>TRUE if item given successfully</returns>
bool SendCreativeInventoryAction(int slot, ItemType itemType, int count, Dictionary<string, object>? nbt);
/// <summary>
/// Send a click container button packet to the server.
/// Used for Enchanting table, Lectern, stone cutter and loom
/// </summary>
/// <param name="windowId">Id of the window being clicked</param>
/// <param name="buttonId">Id of the clicked button</param>
/// <returns>True if packet was successfully sent</returns>
bool ClickContainerButton(int windowId, int buttonId);
/// <summary>
/// Plays animation
/// </summary>

View file

@ -280,6 +280,15 @@ namespace MinecraftClient.Protocol
/// <param name="TimeOfDay">Time of Day</param>
void OnTimeUpdate(long worldAge, long timeOfDay);
/// <summary>
/// When received window properties from server.
///
/// </summary>
/// <param name="inventoryID">Inventory ID</param>
/// <param name="propertyId">Property ID</param>
/// <param name="propertyValue">Property Value</param>
public void OnWindowProperties(byte inventoryID, short propertyId, short propertyValue);
/// <summary>
/// Called when inventory items have been received
/// </summary>
@ -439,5 +448,15 @@ namespace MinecraftClient.Protocol
/// <param name="location">The location of the block.</param>
/// <param name="block">The block</param>
public void OnBlockChange(Location location, Block block);
/// <summary>
/// Send a click container button packet to the server.
/// Used for Enchanting table, Lectern, stone cutter and loom
/// </summary>
/// <param name="windowId">Id of the window being clicked</param>
/// <param name="buttonId">Id of the clicked button</param>
/// <returns>True if packet was successfully sent</returns>
bool ClickContainerButton(int windowId, int buttonId);
}
}

View file

@ -152,6 +152,45 @@ extra.inventory_close=Inventory # {0} closed.
extra.entity_disabled=§cEntities are currently not handled for that MC version.
extra.entity_required=Please enable EntityHandling in the config file first.
# Enchantments
Enchantment.Protection=Protection
Enchantment.FireProtection=Fire Protection
Enchantment.FeatherFalling=Feather Falling
Enchantment.BlastProtection=Blast Protection
Enchantment.ProjectileProtection=Projectile Protection
Enchantment.Respiration=Respiration
Enchantment.AquaAffinity=Aqua Affinity
Enchantment.Thorns=Thorns
Enchantment.DepthStrieder=Depth Strieder
Enchantment.FrostWalker=Frost Walker
Enchantment.BindingCurse=Curse of Binding
Enchantment.SoulSpeed=Soul Speed
Enchantment.Sharpness=Sharpness
Enchantment.Smite=Smite
Enchantment.BaneOfArthropods=Bane of Arthropods
Enchantment.Knockback=Knockback
Enchantment.FireAspect=Fire Aspect
Enchantment.Looting=Looting
Enchantment.Sweeping=Sweeping
Enchantment.Efficency=Efficency
Enchantment.SilkTouch=Silk Touch
Enchantment.Unbreaking=Unbreaking
Enchantment.Fortune=Fortune
Enchantment.Power=Power
Enchantment.Punch=Punch
Enchantment.Flame=Flame
Enchantment.Infinity=Infinity
Enchantment.LuckOfTheSea=Luck of the Sea
Enchantment.Lure=Lure
Enchantment.Loyality=Loyality
Enchantment.Impaling=Impaling
Enchantment.Riptide=Riptide
Enchantment.Channeling=Channeling
Enchantment.Multishot=Multishot
Enchantment.QuickCharge=Quick Charge
Enchantment.Piercing=Piercing
Enchantment.Mending=Mending
Enchantment.VanishingCurse=Curse of Vanishing
[forge]
# Messages from Forge handler
@ -263,6 +302,13 @@ cmd.dig.no_block=No block at this location (Air)
cmd.dig.dig=Attempting to dig block at {0} {1} {2} ({3})
cmd.dig.fail=Failed to start digging block.
# Enchantment Command
cmd.enchant.invalid_slot=Invalid slot provided (Available: top, middle, bottom)!
cmd.enchant.enchanting_table_not_opened=You must open a an enchanting table in order to use this option!
cmd.enchant.clicked=Sent a click to the server, if you have enough levels and if you have placed the items in the correct slots it should enchant!
cmd.enchant.not_clicked=Could not click!
# Entitycmd
cmd.entityCmd.attacked=Entity attacked
cmd.entityCmd.used=Entity used

View file

@ -358,6 +358,16 @@ namespace MinecraftClient
/// <param name="inventoryId"></param>
public virtual void OnInventoryClose(int inventoryId) { }
/// <summary>
/// When received inventory/container/window properties from the server.
/// Used for Frunaces, Enchanting Table, Beacon, Brewing stand, Stone cutter, Loom and Lectern
/// More info about: https://wiki.vg/Protocol#Set_Container_Property
/// </summary>
/// <param name="inventoryID">Inventory ID</param>
/// <param name="propertyId">Property ID</param>
/// <param name="propertyValue">Property Value</param>
public virtual void OnInventoryProperties(byte inventoryID, short propertyId, short propertyValue) { }
/// <summary>
/// Called when a player joined the game
/// </summary>