diff --git a/MinecraftClient/ChatBot.cs b/MinecraftClient/ChatBot.cs index 2091b862..b9996933 100644 --- a/MinecraftClient/ChatBot.cs +++ b/MinecraftClient/ChatBot.cs @@ -220,7 +220,15 @@ namespace MinecraftClient /// /// public virtual void OnMapData(int mapid, byte scale, bool trackingposition, bool locked, int iconcount) { } - + + /// + /// Called when tradeList is received from server + /// + /// Window ID + /// List of trades. + /// Contains Level, Experience, IsRegularVillager and CanRestock . + public virtual void OnTradeList(int windowID, List trades, VillagerInfo villagerInfo) { } + /// /// Called when received a title from the server /// 0 = set title, 1 = set subtitle, 3 = set action bar, 4 = set times and display, 4 = hide, 5 = reset @@ -1225,6 +1233,15 @@ namespace MinecraftClient { return Handler.UpdateSign(location, line1, line2, line3, line4); } + + /// + /// Selects villager trade + /// + /// Trade slot to select, starts at 0. + protected bool SelectTrade(int selectedSlot) + { + return Handler.SelectTrade(selectedSlot); + } /// /// Update command block diff --git a/MinecraftClient/Inventory/VillagerInfo.cs b/MinecraftClient/Inventory/VillagerInfo.cs new file mode 100644 index 00000000..28f793af --- /dev/null +++ b/MinecraftClient/Inventory/VillagerInfo.cs @@ -0,0 +1,13 @@ +namespace MinecraftClient.Mapping +{ + /// + /// Properties of a villager + /// + public class VillagerInfo + { + public int Level { get; set; } + public int Experience { get; set; } + public bool IsRegularVillager { get; set; } + public bool CanRestock { get; set; } + } +} diff --git a/MinecraftClient/Inventory/VillagerTrade.cs b/MinecraftClient/Inventory/VillagerTrade.cs new file mode 100644 index 00000000..4e1948b4 --- /dev/null +++ b/MinecraftClient/Inventory/VillagerTrade.cs @@ -0,0 +1,33 @@ +namespace MinecraftClient.Inventory +{ + /// + /// Represents a trade of a villager + /// + public class VillagerTrade + { + public Item InputItem1; + public Item OutputItem; + public Item InputItem2; + public bool TradeDisabled; + public int NumberOfTradeUses; + public int MaximumNumberOfTradeUses; + public int Xp; + public int SpecialPrice; + public float PriceMultiplier; + public int Demand; + + public VillagerTrade(Item inputItem1, Item outputItem, Item inputItem2, bool tradeDisabled, int numberOfTradeUses, int maximumNumberOfTradeUses, int xp, int specialPrice, float priceMultiplier, int demand) + { + this.InputItem1 = inputItem1; + this.OutputItem = outputItem; + this.InputItem2 = inputItem2; + this.TradeDisabled = tradeDisabled; + this.NumberOfTradeUses = numberOfTradeUses; + this.MaximumNumberOfTradeUses = maximumNumberOfTradeUses; + this.Xp = xp; + this.SpecialPrice = specialPrice; + this.PriceMultiplier = priceMultiplier; + this.Demand = demand; + } + } +} diff --git a/MinecraftClient/Mapping/VillagerInfo.cs b/MinecraftClient/Mapping/VillagerInfo.cs new file mode 100644 index 00000000..7cd38373 --- /dev/null +++ b/MinecraftClient/Mapping/VillagerInfo.cs @@ -0,0 +1,13 @@ +namespace MinecraftClient +{ + /// + /// Information of a villager + /// + public class VillagerInfo + { + public int Level { get; set; } + public int Experience { get; set; } + public bool IsRegularVillager { get; set; } + public bool CanRestock { get; set; } + } +} diff --git a/MinecraftClient/McClient.cs b/MinecraftClient/McClient.cs index 1fe22d9f..6883db9d 100644 --- a/MinecraftClient/McClient.cs +++ b/MinecraftClient/McClient.cs @@ -1449,6 +1449,15 @@ namespace MinecraftClient // TODO Open sign editor first https://wiki.vg/Protocol#Open_Sign_Editor return handler.SendUpdateSign(location, line1, line2, line3, line4); } + + /// + /// Select villager trade + /// + /// The slot of the trade, starts at 0. + public bool SelectTrade(int selectedSlot) + { + return handler.SelectTrade(selectedSlot); + } /// /// Update command block @@ -2246,6 +2255,18 @@ namespace MinecraftClient DispatchBotEvent(bot => bot.OnEntityMetadata(entity, metadata)); } } + + /// + /// Called when tradeList is recieved after interacting with villager + /// + /// Window ID + /// List of trades. + /// Contains Level, Experience, IsRegularVillager and CanRestock . + public void OnTradeList(int windowID, List trades, VillagerInfo villagerInfo) + { + DispatchBotEvent(bot => bot.OnTradeList(windowID, trades, villagerInfo)); + } + #endregion } } diff --git a/MinecraftClient/MinecraftClient.csproj b/MinecraftClient/MinecraftClient.csproj index 3f9cca7a..ef865cd2 100644 --- a/MinecraftClient/MinecraftClient.csproj +++ b/MinecraftClient/MinecraftClient.csproj @@ -212,8 +212,10 @@ + + diff --git a/MinecraftClient/Protocol/Handlers/DataTypes.cs b/MinecraftClient/Protocol/Handlers/DataTypes.cs index 22623975..3cf0cb88 100644 --- a/MinecraftClient/Protocol/Handlers/DataTypes.cs +++ b/MinecraftClient/Protocol/Handlers/DataTypes.cs @@ -610,6 +610,29 @@ namespace MinecraftClient.Protocol.Handlers return data; } + /// + /// Read a single villager trade from a cache of bytes and remove it from the cache + /// + /// The item that was read or NULL for an empty slot + public VillagerTrade ReadNextTrade(Queue cache, ItemPalette itemPalette) + { + Item inputItem1 = ReadNextItemSlot(cache, itemPalette); + Item outputItem = ReadNextItemSlot(cache, itemPalette); + Item inputItem2 = null; + if (ReadNextBool(cache)) //check if villager has second item + { + inputItem2 = ReadNextItemSlot(cache, itemPalette); + } + bool tradeDisabled = ReadNextBool(cache); + int numberOfTradeUses = ReadNextInt(cache); + int maximumNumberOfTradeUses = ReadNextInt(cache); + int xp = ReadNextInt(cache); + int specialPrice = ReadNextInt(cache); + float priceMultiplier = ReadNextFloat(cache); + int demand = ReadNextInt(cache); + return new VillagerTrade(inputItem1, outputItem, inputItem2, tradeDisabled, numberOfTradeUses, maximumNumberOfTradeUses, xp, specialPrice, priceMultiplier, demand); + } + /// /// Build an uncompressed Named Binary Tag blob for sending over the network /// diff --git a/MinecraftClient/Protocol/Handlers/Protocol16.cs b/MinecraftClient/Protocol/Handlers/Protocol16.cs index fc019f47..91619be8 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol16.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol16.cs @@ -845,5 +845,10 @@ namespace MinecraftClient.Protocol.Handlers } catch { return false; } } + + public bool SelectTrade(int selectedSlot) + { + return false; //MC 1.13+ + } } } diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index 990dcb25..7634ba5d 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -432,6 +432,28 @@ namespace MinecraftClient.Protocol.Handlers int iconcount = dataTypes.ReadNextVarInt(packetData); handler.OnMapData(mapid, scale, trackingposition, locked, iconcount); break; + case PacketTypesIn.TradeList: + if ((protocolversion >= MC114Version) && (handler.GetInventoryEnabled())) + { + // MC 1.14 or greater + int windowID = dataTypes.ReadNextVarInt(packetData); + int size = dataTypes.ReadNextByte(packetData); + List trades = new List(); + for (int tradeId = 0; tradeId < size; tradeId++) + { + VillagerTrade trade = dataTypes.ReadNextTrade(packetData, itemPalette); + trades.Add(trade); + } + VillagerInfo villagerInfo = new VillagerInfo() + { + Level = dataTypes.ReadNextVarInt(packetData), + Experience = dataTypes.ReadNextVarInt(packetData), + IsRegularVillager = dataTypes.ReadNextBool(packetData), + CanRestock = dataTypes.ReadNextBool(packetData) + }; + handler.OnTradeList(windowID, trades, villagerInfo); + } + break; case PacketTypesIn.Title: if (protocolversion >= MC18Version) { @@ -1889,5 +1911,24 @@ namespace MinecraftClient.Protocol.Handlers catch (System.IO.IOException) { return false; } catch (ObjectDisposedException) { return false; } } + + public bool SelectTrade(int selectedSlot) + { + // MC 1.13 or greater + if (protocolversion >= MC113Version) + { + try + { + List packet = new List(); + packet.AddRange(dataTypes.GetVarInt(selectedSlot)); + SendPacket(PacketTypesOut.SelectTrade, packet); + return true; + } + catch (SocketException) { return false; } + catch (System.IO.IOException) { return false; } + catch (ObjectDisposedException) { return false; } + } + else { return false; } + } } } diff --git a/MinecraftClient/Protocol/IMinecraftCom.cs b/MinecraftClient/Protocol/IMinecraftCom.cs index c4d8afa3..4770a098 100644 --- a/MinecraftClient/Protocol/IMinecraftCom.cs +++ b/MinecraftClient/Protocol/IMinecraftCom.cs @@ -229,5 +229,11 @@ namespace MinecraftClient.Protocol /// command block mode /// command block flags bool UpdateCommandBlock(Location location, string command, CommandBlockMode mode, CommandBlockFlags flags); + + /// + /// Select villager trade + /// + /// The slot of the trade, starts at 0. + bool SelectTrade(int selectedSlot); } } diff --git a/MinecraftClient/Protocol/IMinecraftComHandler.cs b/MinecraftClient/Protocol/IMinecraftComHandler.cs index 7feff6f0..9c3f57d4 100644 --- a/MinecraftClient/Protocol/IMinecraftComHandler.cs +++ b/MinecraftClient/Protocol/IMinecraftComHandler.cs @@ -330,5 +330,16 @@ 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 tradeList is received from server + /// + /// Window ID + /// List of trades. + /// The level the villager is. + /// The amount of experience the villager has. + /// True if regular villagers and false if the wandering trader. + /// If the villager can restock his trades at a workstation, True for regular villagers and false for the wandering trader. + void OnTradeList(int windowID, List trades, VillagerInfo villagerInfo); } }