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