From 46be73eb3e6595130b2303dfc12a310afa4e27ab Mon Sep 17 00:00:00 2001 From: chsami <23292806+chsami@users.noreply.github.com> Date: Thu, 15 Oct 2026 14:42:41 +0200 Subject: [PATCH] fix(aiofighter): guard null cache inventory setup after equipping slayer gear (2.2.9) addInventorySetup("cache") registers the setup on the client thread, so the immediate lookup could miss it and pass null to setCurrentSlayerInventorySetup, which threw an NPE on getName(). Wait for the cache setup to register before looking it up, and make the setter ignore a null setup. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01LjtEMzQHGuq4M1Mpqk9pPV --- .../microbot/aiofighter/AIOFighterPlugin.java | 6 ++- .../aiofighter/bank/BankerScript.java | 14 +++++- .../bank/BankerScriptCacheSetupTest.java | 49 +++++++++++++++++++ 3 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 src/test/java/net/runelite/client/plugins/microbot/aiofighter/bank/BankerScriptCacheSetupTest.java diff --git a/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterPlugin.java b/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterPlugin.java index 842016ac5f..1df5bc78cc 100644 --- a/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterPlugin.java +++ b/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterPlugin.java @@ -62,7 +62,7 @@ ) @Slf4j public class AIOFighterPlugin extends Plugin { - public static final String version = "2.2.8"; + public static final String version = "2.2.9"; public static boolean needShopping = false; private static final String SET = "Set"; private static final String CENTER_TILE = ColorUtil.wrapWithColorTag("Center Tile", JagexColors.MENU_TARGET); @@ -251,6 +251,10 @@ public static void setSlayerHasTaskWeakness(boolean slayerHasTaskWeakness) { } public static void setCurrentSlayerInventorySetup(InventorySetup currentInventorySetup) { + if (currentInventorySetup == null) { + Microbot.log("Inventory setup not found; keeping current inventory setup"); + return; + } Microbot.log("Setting current inventory setup to: " + currentInventorySetup.getName()); setConfig("currentInventorySetup", currentInventorySetup); } diff --git a/src/main/java/net/runelite/client/plugins/microbot/aiofighter/bank/BankerScript.java b/src/main/java/net/runelite/client/plugins/microbot/aiofighter/bank/BankerScript.java index 7296abcd60..b78be6187c 100644 --- a/src/main/java/net/runelite/client/plugins/microbot/aiofighter/bank/BankerScript.java +++ b/src/main/java/net/runelite/client/plugins/microbot/aiofighter/bank/BankerScript.java @@ -242,6 +242,17 @@ static boolean isSetupResolvable(Collection setupNames, String setupName .anyMatch(name -> name.equalsIgnoreCase(setupName) || name.equals("default")); } + static InventorySetup findSetupByName(Collection setups, String setupName) { + if (setups == null || setupName == null) { + return null; + } + return setups.stream() + .filter(Objects::nonNull) + .filter(setup -> setupName.equalsIgnoreCase(setup.getName())) + .findFirst() + .orElse(null); + } + /** * Checks if the given setup requires any variant of a specific potion but the player does not have it. */ @@ -479,7 +490,8 @@ public void withdrawUpkeepItems(AIOFighterConfig config) { Rs2Bank.withdrawAndEquip(Rs2Slayer.getSlayerTaskProtectiveEquipment()); sleep(3000); inventorySetupsPlugin.addInventorySetup("cache"); - InventorySetup inventorySetup1 = MInventorySetupsPlugin.getInventorySetups().stream().filter(Objects::nonNull).filter(x -> x.getName().equalsIgnoreCase("cache")).findFirst().orElse(null); + sleepUntil(() -> findSetupByName(MInventorySetupsPlugin.getInventorySetups(), "cache") != null, 3000); + InventorySetup inventorySetup1 = findSetupByName(MInventorySetupsPlugin.getInventorySetups(), "cache"); AIOFighterPlugin.setCurrentSlayerInventorySetup(inventorySetup1); } else { ShopScript.shopItems.add(new ShopItem(Rs2Slayer.getSlayerTaskProtectiveEquipment(), 1, Microbot.getRs2ItemManager().getItemId(Rs2Slayer.getSlayerTaskProtectiveEquipment()), ShopType.SLAYER_SHOP)); diff --git a/src/test/java/net/runelite/client/plugins/microbot/aiofighter/bank/BankerScriptCacheSetupTest.java b/src/test/java/net/runelite/client/plugins/microbot/aiofighter/bank/BankerScriptCacheSetupTest.java new file mode 100644 index 0000000000..5df3cea82e --- /dev/null +++ b/src/test/java/net/runelite/client/plugins/microbot/aiofighter/bank/BankerScriptCacheSetupTest.java @@ -0,0 +1,49 @@ +package net.runelite.client.plugins.microbot.aiofighter.bank; + +import net.runelite.client.plugins.microbot.Microbot; +import net.runelite.client.plugins.microbot.aiofighter.AIOFighterPlugin; +import net.runelite.client.plugins.microbot.inventorysetups.InventorySetup; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; + +import java.util.Arrays; +import java.util.Collections; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.when; + +public final class BankerScriptCacheSetupTest { + + private static InventorySetup setup(String name) { + InventorySetup setup = mock(InventorySetup.class); + when(setup.getName()).thenReturn(name); + return setup; + } + + @Test + void findsSetupByNameIgnoringCase() { + InventorySetup cache = setup("Cache"); + assertSame(cache, BankerScript.findSetupByName(Arrays.asList(setup("Melee"), null, setup(null), cache), "cache")); + } + + @Test + void returnsNullWhenSetupNotRegisteredYet() { + assertNull(BankerScript.findSetupByName(Collections.singletonList(setup("Melee")), "cache")); + assertNull(BankerScript.findSetupByName(Collections.emptyList(), "cache")); + assertNull(BankerScript.findSetupByName(null, "cache")); + assertNull(BankerScript.findSetupByName(Collections.singletonList(setup("cache")), null)); + } + + @Test + void settingNullInventorySetupIsIgnored() { + try (MockedStatic microbot = mockStatic(Microbot.class)) { + assertDoesNotThrow(() -> AIOFighterPlugin.setCurrentSlayerInventorySetup(null)); + microbot.verify(Microbot::getConfigManager, never()); + } + } +}