diff --git a/src/main/java/net/runelite/client/plugins/microbot/farmtreerun/FarmTreeRunPlugin.java b/src/main/java/net/runelite/client/plugins/microbot/farmtreerun/FarmTreeRunPlugin.java index 98a3e0aa5e..2780851b2f 100644 --- a/src/main/java/net/runelite/client/plugins/microbot/farmtreerun/FarmTreeRunPlugin.java +++ b/src/main/java/net/runelite/client/plugins/microbot/farmtreerun/FarmTreeRunPlugin.java @@ -30,7 +30,7 @@ ) @Slf4j public class FarmTreeRunPlugin extends Plugin { - public static final String version = "1.2.2"; + public static final String version = "1.2.3"; @Inject private FarmTreeRunConfig config; @Provides diff --git a/src/main/java/net/runelite/client/plugins/microbot/farmtreerun/FarmTreeRunScript.java b/src/main/java/net/runelite/client/plugins/microbot/farmtreerun/FarmTreeRunScript.java index 1833743fcc..f349d18695 100644 --- a/src/main/java/net/runelite/client/plugins/microbot/farmtreerun/FarmTreeRunScript.java +++ b/src/main/java/net/runelite/client/plugins/microbot/farmtreerun/FarmTreeRunScript.java @@ -42,7 +42,6 @@ public class FarmTreeRunScript extends Script { public static net.runelite.client.plugins.microbot.farmtreerun.enums.FarmTreeRunState botStatus; public static boolean test = false; public static Integer compostItemId = null; - private List items = new ArrayList<>(); private final FarmTreeRunPlugin plugin; private final FarmTreeRunConfig config; @@ -558,7 +557,7 @@ private static boolean isInterruption(Throwable error) { } private void bank(FarmTreeRunConfig config) { - items.clear(); + List items = new ArrayList<>(); if (Rs2Bank.openBank() || Rs2Bank.walkToBank()) { sleepUntil(Rs2Bank::isOpen, 5000); if (!Rs2Bank.isOpen()) @@ -704,11 +703,11 @@ private void bank(FarmTreeRunConfig config) { } if (config.travelTablets()) { - addAvailableTravelItem(net.runelite.api.ItemID.VARROCK_TELEPORT, 2); - addAvailableTravelItem(net.runelite.api.ItemID.LUMBRIDGE_TELEPORT, 1); - addAvailableTravelItem(net.runelite.api.ItemID.FALADOR_TELEPORT, 2); - addAvailableTravelItem(net.runelite.api.ItemID.CAMELOT_TELEPORT, 1); - if (!config.preferHouseTravel()) addAvailableTravelItem(net.runelite.api.ItemID.TELEPORT_TO_HOUSE, 3); + addAvailableTravelItem(items, net.runelite.api.ItemID.VARROCK_TELEPORT, 2); + addAvailableTravelItem(items, net.runelite.api.ItemID.LUMBRIDGE_TELEPORT, 1); + addAvailableTravelItem(items, net.runelite.api.ItemID.FALADOR_TELEPORT, 2); + addAvailableTravelItem(items, net.runelite.api.ItemID.CAMELOT_TELEPORT, 1); + if (!config.preferHouseTravel()) addAvailableTravelItem(items, net.runelite.api.ItemID.TELEPORT_TO_HOUSE, 3); } else { items.add(new FarmingItem(ItemID.LAW_RUNE, 10)); items.add(new FarmingItem(ItemID.FIRE_RUNE, 30)); @@ -717,31 +716,25 @@ private void bank(FarmTreeRunConfig config) { items.add(new FarmingItem(ItemID.WATER_RUNE, 30)); } if (config.travelKourendBook()) { - addFirstTravelItem(net.runelite.api.ItemID.BOOK_OF_THE_DEAD, net.runelite.api.ItemID.KHAREDSTS_MEMOIRS); + addFirstTravelItem(items, net.runelite.api.ItemID.BOOK_OF_THE_DEAD, net.runelite.api.ItemID.KHAREDSTS_MEMOIRS); } if (config.preferHouseTravel()) { items.add(new FarmingItem(net.runelite.api.ItemID.TELEPORT_TO_HOUSE, 10)); } if (config.travelQuetzal()) { - addFirstTravelItem(net.runelite.api.ItemID.PERFECTED_QUETZAL_WHISTLEI, + addFirstTravelItem(items, net.runelite.api.ItemID.PERFECTED_QUETZAL_WHISTLEI, net.runelite.api.ItemID.PERFECTED_QUETZAL_WHISTLE, net.runelite.api.ItemID.ENHANCED_QUETZAL_WHISTLE, net.runelite.api.ItemID.BASIC_QUETZAL_WHISTLE); } if (config.travelFairyStaff()) { - addFirstTravelItem(net.runelite.api.ItemID.DRAMEN_STAFF, net.runelite.api.ItemID.LUNAR_STAFF); + addFirstTravelItem(items, net.runelite.api.ItemID.DRAMEN_STAFF, net.runelite.api.ItemID.LUNAR_STAFF); } // TODO: Need to handle what happens if a required item does not exist // Merge entries with the same itemId + noted flag so withdrawal doesn't under-count - Map merged = new LinkedHashMap<>(); - for (FarmingItem item : items) { - long key = ((long) item.getItemId() << 1) | (item.isNoted() ? 1 : 0); - merged.merge(key, item, (a, b) -> - new FarmingItem(a.getItemId(), a.getQuantity() + b.getQuantity(), a.isNoted(), a.isOptional())); - } - items = new ArrayList<>(merged.values()); + items = mergeItems(items); // Deposit only what we don't need: keep desired ids and their noted variants Set keepIds = new HashSet<>(); @@ -824,16 +817,26 @@ private void checkIfPlayerHasItem(int itemId, int quantity, boolean optional) { } } - private void addAvailableTravelItem(int id, int quantity) { + static List mergeItems(List items) { + Map merged = new LinkedHashMap<>(); + for (FarmingItem item : items) { + long key = ((long) item.getItemId() << 1) | (item.isNoted() ? 1 : 0); + merged.merge(key, item, (a, b) -> + new FarmingItem(a.getItemId(), a.getQuantity() + b.getQuantity(), a.isNoted(), a.isOptional())); + } + return new ArrayList<>(merged.values()); + } + + private void addAvailableTravelItem(List items, int id, int quantity) { int available = Rs2Inventory.itemQuantity(id) + Rs2Bank.count(id); if (available > 0) items.add(new FarmingItem(id, Math.min(quantity, available), false, true)); } - private void addFirstTravelItem(int... ids) { + private void addFirstTravelItem(List items, int... ids) { for (int id : ids) { if (Rs2Equipment.isWearing(id)) return; if (Rs2Inventory.hasItem(id) || Rs2Bank.hasItem(id)) { - addAvailableTravelItem(id, 1); + addAvailableTravelItem(items, id, 1); return; } } @@ -1271,7 +1274,6 @@ private void handleExoticGardeners() { @Override public void shutdown() { if(isRunning()) { - items.clear(); super.shutdown(); Microbot.stopPlugin(plugin); } diff --git a/src/test/java/net/runelite/client/plugins/microbot/farmtreerun/FarmTreeRunBankItemsTest.java b/src/test/java/net/runelite/client/plugins/microbot/farmtreerun/FarmTreeRunBankItemsTest.java new file mode 100644 index 0000000000..f83ad9cb07 --- /dev/null +++ b/src/test/java/net/runelite/client/plugins/microbot/farmtreerun/FarmTreeRunBankItemsTest.java @@ -0,0 +1,50 @@ +package net.runelite.client.plugins.microbot.farmtreerun; + +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class FarmTreeRunBankItemsTest { + + @Test + void scriptKeepsNoSharedWithdrawalCollection() { + for (Field field : FarmTreeRunScript.class.getDeclaredFields()) { + if (Modifier.isStatic(field.getModifiers())) continue; + assertFalse(Collection.class.isAssignableFrom(field.getType()), + "FarmTreeRunScript must not share collection state across threads: " + field.getName()); + } + } + + @Test + void mergeSumsSameItemAndNotedFlag() { + List input = new ArrayList<>(); + input.add(new FarmingItem(563, 10)); + input.add(new FarmingItem(554, 30)); + input.add(new FarmingItem(563, 5, false, true)); + input.add(new FarmingItem(995, 100, true)); + input.add(new FarmingItem(995, 50, false)); + + List merged = FarmTreeRunScript.mergeItems(input); + + assertNotSame(input, merged); + assertEquals(5, input.size()); + assertEquals(4, merged.size()); + assertEquals(563, merged.get(0).getItemId()); + assertEquals(15, merged.get(0).getQuantity()); + assertFalse(merged.get(0).isOptional()); + assertEquals(554, merged.get(1).getItemId()); + assertTrue(merged.get(2).isNoted()); + assertEquals(100, merged.get(2).getQuantity()); + assertFalse(merged.get(3).isNoted()); + assertEquals(50, merged.get(3).getQuantity()); + } +}