From 41dab906ace69495a9a6ebec8deb164afef14f3c Mon Sep 17 00:00:00 2001 From: chsami <23292806+chsami@users.noreply.github.com> Date: Thu, 15 Oct 2026 15:01:15 +0200 Subject: [PATCH] fix(MageTrainingArena): guard buyReward against unloaded reward shop widget (1.1.9) Rs2Widget.getWidget(197, 11) returns null when the reward shop child is not built yet or the shop has just closed, which threw an NPE inside waitForInventoryChanges. Return early when the container is null and skip the click and confirm when the reward item is not in the list. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01LjtEMzQHGuq4M1Mpqk9pPV --- .../MageTrainingArenaPlugin.java | 2 +- .../MageTrainingArenaScript.java | 19 ++++++++--- .../MageTrainingArenaScriptTest.java | 34 +++++++++++++++++++ 3 files changed, 50 insertions(+), 5 deletions(-) create mode 100644 src/test/java/net/runelite/client/plugins/microbot/magetrainingarena/MageTrainingArenaScriptTest.java diff --git a/src/main/java/net/runelite/client/plugins/microbot/magetrainingarena/MageTrainingArenaPlugin.java b/src/main/java/net/runelite/client/plugins/microbot/magetrainingarena/MageTrainingArenaPlugin.java index e03170c3ca..fcaf3ff779 100644 --- a/src/main/java/net/runelite/client/plugins/microbot/magetrainingarena/MageTrainingArenaPlugin.java +++ b/src/main/java/net/runelite/client/plugins/microbot/magetrainingarena/MageTrainingArenaPlugin.java @@ -26,7 +26,7 @@ ) @Slf4j public class MageTrainingArenaPlugin extends Plugin { - public static final String version = "1.1.8"; + public static final String version = "1.1.9"; @Inject private MageTrainingArenaConfig config; diff --git a/src/main/java/net/runelite/client/plugins/microbot/magetrainingarena/MageTrainingArenaScript.java b/src/main/java/net/runelite/client/plugins/microbot/magetrainingarena/MageTrainingArenaScript.java index b0b0bbeddb..cb2f03012f 100644 --- a/src/main/java/net/runelite/client/plugins/microbot/magetrainingarena/MageTrainingArenaScript.java +++ b/src/main/java/net/runelite/client/plugins/microbot/magetrainingarena/MageTrainingArenaScript.java @@ -6,6 +6,7 @@ import net.runelite.api.gameval.*; import net.runelite.api.coords.LocalPoint; import net.runelite.api.coords.WorldPoint; +import net.runelite.api.widgets.Widget; import net.runelite.client.plugins.microbot.Microbot; import net.runelite.client.plugins.microbot.Script; import net.runelite.client.plugins.microbot.globval.enums.InterfaceTab; @@ -584,10 +585,12 @@ private void buyReward(Rewards reward) { } Rs2Inventory.waitForInventoryChanges(() -> { - var rewardWidgets = Rs2Widget.getWidget(197, 11).getDynamicChildren(); - if (rewardWidgets == null) return; - var widget = Arrays.stream(rewardWidgets).filter(x -> x.getItemId() == reward.getItemId()).findFirst().orElse(null); - Rs2Widget.clickWidgetFast(widget, Arrays.asList(rewardWidgets).indexOf(widget)); + var rewardContainer = Rs2Widget.getWidget(197, 11); + if (rewardContainer == null) return; + var rewardWidgets = rewardContainer.getDynamicChildren(); + var index = findRewardIndex(rewardWidgets, reward.getItemId()); + if (index < 0) return; + Rs2Widget.clickWidgetFast(rewardWidgets[index], index); sleepGaussian(600, 150); Rs2Widget.clickWidget(197, 9); sleepGaussian(600, 150); @@ -605,6 +608,14 @@ private void buyReward(Rewards reward) { } } + static int findRewardIndex(Widget[] rewardWidgets, int itemId) { + if (rewardWidgets == null) return -1; + for (int i = 0; i < rewardWidgets.length; i++) { + if (rewardWidgets[i] != null && rewardWidgets[i].getItemId() == itemId) return i; + } + return -1; + } + public static Rooms getCurrentRoom() { for (var room : Rooms.values()) { if (room == Rooms.TELEKINETIC && Arrays.stream(TelekineticRooms.values()).anyMatch(x -> Rs2Player.getWorldLocation().distanceTo(x.getArea()) == 0) diff --git a/src/test/java/net/runelite/client/plugins/microbot/magetrainingarena/MageTrainingArenaScriptTest.java b/src/test/java/net/runelite/client/plugins/microbot/magetrainingarena/MageTrainingArenaScriptTest.java new file mode 100644 index 0000000000..b4e4aa52e8 --- /dev/null +++ b/src/test/java/net/runelite/client/plugins/microbot/magetrainingarena/MageTrainingArenaScriptTest.java @@ -0,0 +1,34 @@ +package net.runelite.client.plugins.microbot.magetrainingarena; + +import net.runelite.api.widgets.Widget; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +class MageTrainingArenaScriptTest { + + private static Widget item(int itemId) { + Widget widget = mock(Widget.class); + when(widget.getItemId()).thenReturn(itemId); + return widget; + } + + @Test + void unloadedRewardListHasNoReward() { + assertEquals(-1, MageTrainingArenaScript.findRewardIndex(null, 6908)); + } + + @Test + void missingRewardIsNotClicked() { + Widget[] widgets = {item(6908), item(6910)}; + assertEquals(-1, MageTrainingArenaScript.findRewardIndex(widgets, 6912)); + } + + @Test + void matchingRewardReturnsItsIndex() { + Widget[] widgets = {item(6908), null, item(6910)}; + assertEquals(2, MageTrainingArenaScript.findRewardIndex(widgets, 6910)); + } +}