From 083ddabb85d0acab1127f2c70765ef76ad0631fb Mon Sep 17 00:00:00 2001 From: Chris Xiong Date: Sun, 21 Apr 2024 11:30:03 -0400 Subject: 1.20.4 + AutoTrade. --- .../chrisoft/trashyaddon/modules/AutoTrade.java | 330 +++++++++++++++++++++ 1 file changed, 330 insertions(+) create mode 100644 src/main/java/org/chrisoft/trashyaddon/modules/AutoTrade.java (limited to 'src/main/java/org/chrisoft/trashyaddon/modules/AutoTrade.java') diff --git a/src/main/java/org/chrisoft/trashyaddon/modules/AutoTrade.java b/src/main/java/org/chrisoft/trashyaddon/modules/AutoTrade.java new file mode 100644 index 0000000..5cfacdc --- /dev/null +++ b/src/main/java/org/chrisoft/trashyaddon/modules/AutoTrade.java @@ -0,0 +1,330 @@ +package org.chrisoft.trashyaddon.modules; + +import meteordevelopment.meteorclient.events.game.OpenScreenEvent; +import meteordevelopment.meteorclient.events.packets.PacketEvent; +import meteordevelopment.meteorclient.events.world.TickEvent; +import meteordevelopment.meteorclient.settings.*; +import meteordevelopment.meteorclient.systems.modules.Categories; +import meteordevelopment.meteorclient.systems.modules.Module; +import meteordevelopment.meteorclient.utils.player.FindItemResult; +import meteordevelopment.meteorclient.utils.player.InvUtils; +import meteordevelopment.orbit.EventHandler; +import net.minecraft.client.gui.screen.ingame.MerchantScreen; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.network.packet.s2c.play.ScreenHandlerSlotUpdateS2CPacket; +import net.minecraft.network.packet.s2c.play.SetTradeOffersS2CPacket; +import net.minecraft.village.TradeOffer; +import net.minecraft.village.TradeOfferList; +import org.chrisoft.trashyaddon.mixin.MerchantScreenAccessor; + +import java.util.ArrayList; +import java.util.Formatter; +import java.util.List; +import java.util.Optional; + +public class AutoTrade extends Module { + private final SettingGroup sgGeneral = settings.getDefaultGroup(); + private static final List allSellItems = List.of( + Items.COAL, + Items.CHICKEN, + Items.PORKCHOP, + Items.RABBIT, + Items.MUTTON, + Items.BEEF, + Items.DRIED_KELP_BLOCK, + Items.SWEET_BERRIES, + Items.IRON_INGOT, + Items.DIAMOND, + Items.PAPER, + Items.GLASS_PANE, + Items.ROTTEN_FLESH, + Items.GOLD_INGOT, + Items.RABBIT_FOOT, + Items.SCUTE, + Items.GLASS_BOTTLE, + Items.NETHER_WART, + Items.WHEAT, + Items.POTATO, + Items.CARROT, + Items.BEETROOT, + Items.PUMPKIN, + Items.MELON, + Items.STRING, + Items.COD, + Items.SALMON, + Items.TROPICAL_FISH, + Items.PUFFERFISH, + Items.STICK, + Items.FLINT, + Items.FEATHER, + Items.TRIPWIRE_HOOK, + Items.LEATHER, + Items.RABBIT_HIDE, + Items.BOOK, + Items.INK_SAC, + Items.CLAY_BALL, + Items.STONE, + Items.GRANITE, + Items.ANDESITE, + Items.DIORITE, + Items.QUARTZ + ); + + private static final List allBuyItems = List.of( + Items.BELL, + Items.COOKED_PORKCHOP, + Items.COOKED_CHICKEN, + Items.MAP, + Items.ITEM_FRAME, + Items.REDSTONE, + Items.LAPIS_LAZULI, + Items.GLOWSTONE, + Items.ENDER_PEARL, + Items.EXPERIENCE_BOTTLE, + Items.BREAD, + Items.PUMPKIN_PIE, + Items.APPLE, + Items.COOKIE, + Items.GOLDEN_CARROT, + Items.GLISTERING_MELON_SLICE, + Items.CAMPFIRE, + Items.ARROW, + Items.BOOKSHELF, + Items.LANTERN, + Items.GLASS, + Items.CLOCK, + Items.COMPASS, + Items.NAME_TAG, + Items.BRICK, + Items.CHISELED_STONE_BRICKS, + Items.DRIPSTONE_BLOCK, + Items.POLISHED_ANDESITE, + Items.POLISHED_DIORITE, + Items.POLISHED_GRANITE, + Items.QUARTZ_PILLAR, + Items.QUARTZ_BLOCK, + Items.PAINTING + ); + + private final Setting> sellsSetting = sgGeneral.add(new ItemListSetting.Builder() + .name("sells") + .description("Items to automatically SELL TO villagers.") + .filter((item) -> allSellItems.contains(item)) + .build() + ); + private final Setting> buysSetting = sgGeneral.add(new ItemListSetting.Builder() + .name("buys") + .description("Items to automatically BUY FROM villagers.") + .filter((item) -> allBuyItems.contains(item)) + .build() + ); + private final Setting interactionRate = sgGeneral.add(new IntSetting.Builder() + .name("Interaction Rate") + .description("Number of ticks between interactions.") + .min(0) + .max(10) + .build() + ); + private final Setting autoClose = sgGeneral.add(new BoolSetting.Builder() + .name("Auto Close") + .description("Close trading screen after any successful trades.") + .build() + ); + private final Setting logSummary = sgGeneral.add(new BoolSetting.Builder() + .name("Log Summary") + .description("Give a summary of what has been traded once it finishes trading.") + .build() + ); + + private TradeOfferList offers; + private MerchantScreen screen; + private int currentOffer; + private int ticksRemaining; + private int countBefore; + private int countAfter; + + private enum WaitState { + None, + WaitingForOutput, + WaitingForInventoryUpdate, + WaitingForFinalize + }; + private WaitState waitState; + private ArrayList itemsSold; + private ArrayList itemsBought; + + + public AutoTrade() { + super(Categories.World, "Auto Trade", "Help prevent getting arthritis from excessively trading with villagers."); + itemsSold = new ArrayList<>(); + itemsBought = new ArrayList<>(); + waitState = WaitState.None; + } + + /* + @Override + public String getInfoString() { + return waitState.toString() + " " + ticksRemaining; + } + */ + + private boolean isOfferEligible(TradeOffer o) { + // I don't give a SHIT to trades that use the second slot + // well actually I do, but I don't need THAT many enchanted books... + if (!o.getSecondBuyItem().getItem().equals(Items.AIR)) + return false; + if (o.isDisabled()) + return false; + ItemStack mbuy = o.getAdjustedFirstBuyItem(); + ItemStack msell = o.getSellItem(); + List sells = this.sellsSetting.get(); + List buys = this.buysSetting.get(); + if (!sells.contains(mbuy.getItem()) && !buys.contains(msell.getItem())) + return false; + FindItemResult rs = InvUtils.find((stack) -> stack.getItem().equals(mbuy.getItem()) && stack.getCount() >= mbuy.getCount()); + if (!rs.found()) + return false; + return true; + } + + private void selectTrade() { + while (currentOffer < offers.size() && !isOfferEligible(offers.get(currentOffer))) + ++currentOffer; + if (currentOffer >= offers.size()) { + ticksRemaining = interactionRate.get(); + waitState = WaitState.WaitingForFinalize; + return; + } + waitState = WaitState.WaitingForOutput; + ticksRemaining = -0xdead; + TradeOffer o = offers.get(currentOffer); + Item tradedItem = o.getSellItem().getItem().equals(Items.EMERALD) ? + o.getAdjustedFirstBuyItem().getItem() : o.getSellItem().getItem(); + countBefore = screen.getScreenHandler().slots.stream() + .map(slot -> slot.getStack()) + .filter(s -> s.getItem().equals(tradedItem)) + .map(s -> s.getCount()) + .reduce(0, Integer::sum); + ((MerchantScreenAccessor)screen).setSelectedIndex(currentOffer); + ((MerchantScreenAccessor)screen).invokeSyncRecipeIndex(); + } + + private void finalizeCurrentTrade() { + TradeOffer o = offers.get(currentOffer); + Item tradedItem = o.getSellItem().getItem().equals(Items.EMERALD) ? + o.getAdjustedFirstBuyItem().getItem() : o.getSellItem().getItem(); + countAfter = screen.getScreenHandler().slots.stream() + .map(slot -> slot.getStack()) + .filter(s -> s.getItem().equals(tradedItem)) + .map(s -> s.getCount()) + .reduce(0, Integer::sum); + ArrayList itemsTraded = countAfter < countBefore ? itemsSold : itemsBought; + int count = java.lang.Math.abs(countAfter - countBefore); + Optional t = itemsTraded.stream().filter(s -> s.getItem().equals(tradedItem)).findFirst(); + if (t.isPresent()) { + t.get().setCount(t.get().getCount() + count); + } + else { + ItemStack s = new ItemStack(tradedItem, count); + itemsTraded.add(s); + } + } + + private void endTrading() { + if (logSummary.get()) { + if (!itemsSold.isEmpty()) { + Formatter f = new Formatter(); + f.format("Item(s) sold:"); + boolean first = true; + for (ItemStack i : itemsSold) { + f.format("%s%s*%d", first ? " " : ", ", i.getName().getString(), i.getCount()); + first = false; + } + info(f.toString()); + } + if (!itemsBought.isEmpty()) { + Formatter f = new Formatter(); + f.format("Item(s) bought:"); + boolean first = true; + for (ItemStack i : itemsBought) { + f.format("%s%s*%d", first ? " " : ", ", i.getName().getString(), i.getCount()); + first = false; + } + info(f.toString()); + } + } + if (autoClose.get() && (!itemsBought.isEmpty() || !itemsSold.isEmpty())) + screen.close(); + screen = null; + } + @EventHandler + private void onTick(TickEvent.Pre event) { + if (ticksRemaining > 0) { + --ticksRemaining; + return; + } + if (offers != null && screen != null) { + if (waitState == WaitState.WaitingForFinalize) { + endTrading(); + return; + } + if (waitState != WaitState.None && currentOffer < offers.size() && offers.get(currentOffer).isDisabled()) { + finalizeCurrentTrade(); + waitState = WaitState.None; + ticksRemaining = interactionRate.get(); + return; + } + if (ticksRemaining == -0xdead) + return; + switch (waitState) { + case None: selectTrade(); break; + case WaitingForOutput: + InvUtils.shiftClick().slotId(2); + waitState = WaitState.WaitingForInventoryUpdate; + ticksRemaining = -0xdead; + break; + case WaitingForInventoryUpdate: + finalizeCurrentTrade(); + waitState = WaitState.None; + ticksRemaining = interactionRate.get(); + break; + } + } + } + + @EventHandler + private void onReceivePacket(PacketEvent.Receive e) { + if (e.packet instanceof SetTradeOffersS2CPacket p) { + this.offers = p.getOffers(); + currentOffer = 0; + waitState = WaitState.None; + ticksRemaining = 0; + itemsSold.clear(); + itemsBought.clear(); + } else if (e.packet instanceof ScreenHandlerSlotUpdateS2CPacket p) { + if (screen == null || p.getSyncId() != screen.getScreenHandler().syncId || p.getSlot() != 2) + return; + if (waitState == WaitState.WaitingForOutput) { + ticksRemaining = interactionRate.get(); + } + if (waitState == WaitState.WaitingForInventoryUpdate) { + if (!p.getStack().isEmpty()) + waitState = WaitState.WaitingForOutput; + ticksRemaining = interactionRate.get(); + } + } + } + + @EventHandler + private void onOpenScreen(OpenScreenEvent e) { + if (e.screen == null) { + this.screen = null; + this.offers = null; + } + else if (e.screen instanceof MerchantScreen s) { + this.screen = s; + } + } +} -- cgit v1.2.3