aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Chris Xiong <chirs241097@gmail.com> 2024-04-21 11:30:03 -0400
committerGravatar Chris Xiong <chirs241097@gmail.com> 2024-04-21 11:30:03 -0400
commit083ddabb85d0acab1127f2c70765ef76ad0631fb (patch)
tree584d82d42de265cc35290dadc43c08a113912749 /src
parent31aa0a803069513d32e242a65c4d7ad1daf99355 (diff)
downloadmeteor-trashy-addon-083ddabb85d0acab1127f2c70765ef76ad0631fb.tar.xz
1.20.4 + AutoTrade.
Diffstat (limited to 'src')
-rw-r--r--src/main/java/org/chrisoft/trashyaddon/Addon.java4
-rw-r--r--src/main/java/org/chrisoft/trashyaddon/mixin/MerchantScreenAccessor.java14
-rw-r--r--src/main/java/org/chrisoft/trashyaddon/modules/AutoTrade.java330
-rw-r--r--src/main/resources/addon.mixins.json4
-rw-r--r--src/main/resources/assets/template/icon.pngbin2137 -> 2362 bytes
5 files changed, 350 insertions, 2 deletions
diff --git a/src/main/java/org/chrisoft/trashyaddon/Addon.java b/src/main/java/org/chrisoft/trashyaddon/Addon.java
index a956b6c..eb197ff 100644
--- a/src/main/java/org/chrisoft/trashyaddon/Addon.java
+++ b/src/main/java/org/chrisoft/trashyaddon/Addon.java
@@ -1,9 +1,11 @@
package org.chrisoft.trashyaddon;
import org.chrisoft.trashyaddon.commands.*;
+import org.chrisoft.trashyaddon.modules.*;
import com.mojang.logging.LogUtils;
import meteordevelopment.meteorclient.addons.MeteorAddon;
import meteordevelopment.meteorclient.commands.Commands;
+import meteordevelopment.meteorclient.systems.modules.Modules;
import org.slf4j.Logger;
public class Addon extends MeteorAddon {
@@ -16,7 +18,7 @@ public class Addon extends MeteorAddon {
LOG.info("Initializing Meteor Trash Addons");
// Modules
- //Modules.get().add(new ModuleExample());
+ Modules.get().add(new AutoTrade());
// Commands
Commands.add(new EntityDataCommand());
diff --git a/src/main/java/org/chrisoft/trashyaddon/mixin/MerchantScreenAccessor.java b/src/main/java/org/chrisoft/trashyaddon/mixin/MerchantScreenAccessor.java
new file mode 100644
index 0000000..8050ad3
--- /dev/null
+++ b/src/main/java/org/chrisoft/trashyaddon/mixin/MerchantScreenAccessor.java
@@ -0,0 +1,14 @@
+package org.chrisoft.trashyaddon.mixin;
+
+import net.minecraft.client.gui.screen.ingame.MerchantScreen;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.gen.Accessor;
+import org.spongepowered.asm.mixin.gen.Invoker;
+
+@Mixin(MerchantScreen.class)
+public interface MerchantScreenAccessor {
+ @Accessor("selectedIndex")
+ public void setSelectedIndex(int selectedIndex);
+ @Invoker("syncRecipeIndex")
+ public void invokeSyncRecipeIndex();
+}
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<Item> 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<Item> 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<List<Item>> sellsSetting = sgGeneral.add(new ItemListSetting.Builder()
+ .name("sells")
+ .description("Items to automatically SELL TO villagers.")
+ .filter((item) -> allSellItems.contains(item))
+ .build()
+ );
+ private final Setting<List<Item>> buysSetting = sgGeneral.add(new ItemListSetting.Builder()
+ .name("buys")
+ .description("Items to automatically BUY FROM villagers.")
+ .filter((item) -> allBuyItems.contains(item))
+ .build()
+ );
+ private final Setting<Integer> interactionRate = sgGeneral.add(new IntSetting.Builder()
+ .name("Interaction Rate")
+ .description("Number of ticks between interactions.")
+ .min(0)
+ .max(10)
+ .build()
+ );
+ private final Setting<Boolean> autoClose = sgGeneral.add(new BoolSetting.Builder()
+ .name("Auto Close")
+ .description("Close trading screen after any successful trades.")
+ .build()
+ );
+ private final Setting<Boolean> 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<ItemStack> itemsSold;
+ private ArrayList<ItemStack> 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<Item> sells = this.sellsSetting.get();
+ List<Item> 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<ItemStack> itemsTraded = countAfter < countBefore ? itemsSold : itemsBought;
+ int count = java.lang.Math.abs(countAfter - countBefore);
+ Optional<ItemStack> 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;
+ }
+ }
+}
diff --git a/src/main/resources/addon.mixins.json b/src/main/resources/addon.mixins.json
index 7fbd83d..9ddc8ba 100644
--- a/src/main/resources/addon.mixins.json
+++ b/src/main/resources/addon.mixins.json
@@ -2,7 +2,9 @@
"required": true,
"package": "org.chrisoft.trashyaddon.mixin",
"compatibilityLevel": "JAVA_17",
- "client": [],
+ "client": [
+ "MerchantScreenAccessor"
+ ],
"injectors": {
"defaultRequire": 1
}
diff --git a/src/main/resources/assets/template/icon.png b/src/main/resources/assets/template/icon.png
index 3fb2e7f..2684041 100644
--- a/src/main/resources/assets/template/icon.png
+++ b/src/main/resources/assets/template/icon.png
Binary files differ