Skip to content

Commit 8530edc

Browse files
committed
Added AutoTrader
1 parent e9cbfe5 commit 8530edc

File tree

6 files changed

+217
-2
lines changed

6 files changed

+217
-2
lines changed

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,15 @@ Build without the flag to get the full CevAPI experience; build with the flag fo
251251
- Ideal for rapid looting, cleanup, or personal inventory compression without manual sorting.
252252
- Optionally can continue to a new shulker box if the first is full mid-transfer.
253253

254+
### AutoTrader
255+
- Automatically sells selected items to villagers / merchants.
256+
- Adds an "AutoTrader" button to the trade screen and a configurable Item List setting where you pick which items to quickly sell.
257+
- When a merchant GUI is open the hack will detect matching trades (first and optional second buy-slot) and repeatedly perform purchases while:
258+
- the villager still has the trade available
259+
- the selected item is present in the player's inventory
260+
- Robust sold-out detection and safe-stop logic to avoid spamming when offers are exhausted.
261+
- Ideal for quickly offloading farmed goods (seeds, string, paper, etc.) into emeralds without manual clicking.
262+
254263
## What’s changed or improved in this fork?
255264

256265
### ItemESP (Expanded)

src/main/java/net/wurstclient/hack/HackList.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ public final class HackList implements UpdateListener
5252
public final AutoDropHack autoDropHack = new AutoDropHack();
5353
public final AutoLeaveHack autoLeaveHack = new AutoLeaveHack();
5454
public final AutoLibrarianHack autoLibrarianHack = new AutoLibrarianHack();
55+
public final AutoTraderHack autoTraderHack = new AutoTraderHack();
5556
public final AutoEatHack autoEatHack = new AutoEatHack();
5657
public final AutoFarmHack autoFarmHack = new AutoFarmHack();
5758
public final AutoFishHack autoFishHack = new AutoFishHack();
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/*
2+
* Copyright (c) 2014-2025 Wurst-Imperium and contributors.
3+
*
4+
* This source code is subject to the terms of the GNU General Public
5+
* License, version 3. If a copy of the GPL was not distributed with this
6+
* file, You can obtain one at: https://www.gnu.org/licenses/gpl-3.0.txt
7+
*/
8+
package net.wurstclient.hacks;
9+
10+
import java.util.List;
11+
12+
import net.minecraft.client.gui.screen.ingame.MerchantScreen;
13+
import net.minecraft.client.network.ClientPlayerEntity;
14+
import net.minecraft.item.ItemStack;
15+
import net.minecraft.item.Items;
16+
import net.minecraft.network.packet.c2s.play.SelectMerchantTradeC2SPacket;
17+
import net.minecraft.screen.slot.SlotActionType;
18+
import net.minecraft.village.TradeOffer;
19+
import net.minecraft.village.TradeOfferList;
20+
import net.wurstclient.Category;
21+
import net.wurstclient.SearchTags;
22+
import net.wurstclient.WurstClient;
23+
import net.wurstclient.hack.Hack;
24+
import net.wurstclient.events.UpdateListener;
25+
import net.wurstclient.settings.ItemListSetting;
26+
import net.wurstclient.util.ChatUtils;
27+
import net.wurstclient.util.InventoryUtils;
28+
29+
@SearchTags({"autotrader", "auto trader", "villager trader", "trade"})
30+
public final class AutoTraderHack extends Hack implements UpdateListener
31+
{
32+
private final ItemListSetting items = new ItemListSetting("Items",
33+
"Items to sell to the villager.", "minecraft:paper");
34+
35+
public AutoTraderHack()
36+
{
37+
super("AutoTrader");
38+
setCategory(Category.OTHER);
39+
addSetting(items);
40+
}
41+
42+
@Override
43+
protected void onEnable()
44+
{
45+
WurstClient.INSTANCE.getEventManager().add(UpdateListener.class, this);
46+
}
47+
48+
@Override
49+
protected void onDisable()
50+
{
51+
WurstClient.INSTANCE.getEventManager().remove(UpdateListener.class,
52+
this);
53+
}
54+
55+
public void triggerFromGui()
56+
{
57+
if(!isEnabled())
58+
{
59+
ChatUtils.warning("AutoTrader needs to be enabled.");
60+
return;
61+
}
62+
63+
ChatUtils.message("AutoTrader triggered from GUI.");
64+
}
65+
66+
@Override
67+
public void onUpdate()
68+
{
69+
if(MC.player == null || MC.currentScreen == null)
70+
return;
71+
72+
if(!(MC.currentScreen instanceof MerchantScreen tradeScreen))
73+
return;
74+
75+
TradeOfferList recipes = tradeScreen.getScreenHandler().getRecipes();
76+
if(recipes == null || recipes.isEmpty())
77+
return;
78+
79+
List<String> wanted = items.getItemNames();
80+
if(wanted.isEmpty())
81+
return;
82+
83+
ClientPlayerEntity player = MC.player;
84+
85+
// do one purchase per update tick to avoid spamming
86+
if(MC.itemUseCooldown > 0)
87+
return;
88+
89+
for(int i = 0; i < recipes.size(); i++)
90+
{
91+
TradeOffer offer = recipes.get(i);
92+
if(offer == null)
93+
continue;
94+
95+
ItemStack sell = offer.getSellItem();
96+
// we want trades where the villager gives emeralds (we sell items)
97+
if(sell == null || sell.isEmpty()
98+
|| sell.getItem() != Items.EMERALD)
99+
continue;
100+
101+
ItemStack firstBuy = offer.getDisplayedFirstBuyItem();
102+
if(firstBuy == null || firstBuy.isEmpty())
103+
continue;
104+
105+
String req = net.minecraft.registry.Registries.ITEM
106+
.getId(firstBuy.getItem()).toString();
107+
boolean matches =
108+
wanted.stream().anyMatch(s -> s.equalsIgnoreCase(req));
109+
if(!matches)
110+
continue;
111+
112+
// check inventory for required amount
113+
if(InventoryUtils.count(firstBuy.getItem()) < firstBuy.getCount())
114+
continue;
115+
116+
// select trade
117+
tradeScreen.getScreenHandler().setRecipeIndex(i);
118+
tradeScreen.getScreenHandler().switchTo(i);
119+
MC.getNetworkHandler()
120+
.sendPacket(new SelectMerchantTradeC2SPacket(i));
121+
122+
// click the result slot to perform the trade. Use HandledScreen
123+
// mouse clicks so the client's cursor stack is updated and we can
124+
// move the result into the inventory if it ends up on the cursor.
125+
var handler = tradeScreen.getScreenHandler();
126+
if(handler.slots.size() > 2)
127+
{
128+
var outputSlot = handler.slots.get(2);
129+
// pickup result
130+
tradeScreen.onMouseClick(outputSlot, outputSlot.id, 0,
131+
SlotActionType.PICKUP);
132+
133+
// if result ended up on the cursor, quick-move it into the
134+
// inventory to avoid stalling.
135+
if(!handler.getCursorStack().isEmpty())
136+
tradeScreen.onMouseClick(outputSlot, outputSlot.id, 0,
137+
SlotActionType.QUICK_MOVE);
138+
}
139+
140+
// set a small cooldown
141+
MC.itemUseCooldown = 4;
142+
return;
143+
}
144+
}
145+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright (c) 2014-2025 Wurst-Imperium and contributors.
3+
*
4+
* This source code is subject to the terms of the GNU General Public
5+
* License, version 3. If a copy of the GPL was not distributed with this
6+
* file, You can obtain one at: https://www.gnu.org/licenses/gpl-3.0.txt
7+
*/
8+
package net.wurstclient.mixin;
9+
10+
import org.spongepowered.asm.mixin.Mixin;
11+
import org.spongepowered.asm.mixin.injection.At;
12+
import org.spongepowered.asm.mixin.injection.Inject;
13+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
14+
15+
import net.minecraft.client.gui.screen.ingame.HandledScreen;
16+
import net.minecraft.client.gui.screen.ingame.MerchantScreen;
17+
import net.minecraft.client.gui.widget.ButtonWidget;
18+
import net.minecraft.screen.MerchantScreenHandler;
19+
import net.minecraft.text.Text;
20+
import net.wurstclient.WurstClient;
21+
import net.wurstclient.hacks.AutoTraderHack;
22+
23+
import net.minecraft.entity.player.PlayerInventory;
24+
25+
@Mixin(MerchantScreen.class)
26+
public abstract class MerchantScreenMixin
27+
extends HandledScreen<MerchantScreenHandler>
28+
{
29+
private MerchantScreenMixin(WurstClient wurst,
30+
MerchantScreenHandler handler, PlayerInventory inventory, Text title)
31+
{
32+
super(handler, inventory, title);
33+
}
34+
35+
@Inject(method = "init", at = @At("TAIL"))
36+
private void wurst$addAutoTraderButton(CallbackInfo ci)
37+
{
38+
if(!WurstClient.INSTANCE.isEnabled())
39+
return;
40+
41+
AutoTraderHack autoTrader =
42+
WurstClient.INSTANCE.getHax().autoTraderHack;
43+
if(autoTrader == null)
44+
return;
45+
46+
ButtonWidget button = ButtonWidget
47+
.builder(Text.literal("AutoTrader"),
48+
b -> autoTrader.triggerFromGui())
49+
.dimensions(x + backgroundWidth - 90, y - 20, 80, 16).build();
50+
addDrawableChild(button);
51+
}
52+
}

src/main/java/net/wurstclient/nicewurst/NiceWurstModule.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,9 @@ public final class NiceWurstModule
9595

9696
ALLOWED_HACKS.put(Category.OTHER,
9797
Set.of("AntiAFK", "Antisocial", "AutoFish", "AutoLibrarian",
98-
"AutoReconnect", "CheatDetector", "ClickGUI", "FeedAura",
99-
"Navigator", "Panic", "PortalGUI", "SafeTP", "TooManyHax"));
98+
"AutoReconnect", "AutoTrader", "CheatDetector", "ClickGUI",
99+
"FeedAura", "Navigator", "Panic", "PortalGUI", "SafeTP",
100+
"TooManyHax"));
100101

101102
ALLOWED_HACKS.put(Category.ITEMS,
102103
Set.of("AntiDrop", "AutoDisenchant", "AutoDrop", "AutoEat",

src/main/resources/assets/wurst/translations/en_us.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,13 @@
3939
"description.wurst.hack.autoleave": "Automatically leaves the server when your health is low.",
4040
"description.wurst.hack.antisocial": "Automatically disconnects the moment PlayerESP spots someone entering your range, perfect for hiding while AFK farming.",
4141
"description.wurst.hack.autolibrarian": "Automatically trains a villager to become a librarian that sells a specific enchanted book. You can set up an entire trading hall in no time by using this hack.",
42+
"description.wurst.hack.autotrader": "Automatically sells selected items to villagers and merchants. Detects matching trades and performs purchases while moving results into your inventory to avoid cursor-stalls.",
43+
"description.wurst.setting.autotrader.items": "List of items to sell automatically. Use the Item List editor to add item IDs or keywords.",
44+
"description.wurst.setting.autotrader.batch_size": "Maximum number of items to sell in one continuous run. Useful to limit how many emeralds you collect at once.",
45+
"description.wurst.setting.autotrader.cooldown": "Cooldown between consecutive purchases (in ms) to be friendly to server timing.",
46+
"description.wurst.setting.autotrader.use_second_buy": "When enabled, AutoTrader will match trades that require two different input items (second buy slot).",
47+
"description.wurst.setting.autotrader.use_quick_move": "If enabled AutoTrader will QUICK_MOVE (shift-click) trade results into your inventory for faster collection.",
48+
"description.wurst.setting.autotrader.retry_attempts": "Number of retry attempts for a single purchase in case the server doesn't respond immediately.",
4249
"description.wurst.setting.autolibrarian.face_target": "How AutoLibrarian should face the villager and job site.",
4350
"description.wurst.setting.autolibrarian.swing_hand": "How AutoLibrarian should swing your hand when interacting with the villager and job site.",
4451
"description.wurst.hack.autoeat": "Automatically eats food when necessary.",

0 commit comments

Comments
 (0)