From 3b3d02a93a2b0d10bec5163a6dcc5650a32d2749 Mon Sep 17 00:00:00 2001 From: chsami Date: Wed, 15 Apr 2026 13:33:52 +0200 Subject: [PATCH 1/2] refactor(ui): simplify tab selection logic using a map --- .../microbot/globval/enums/InterfaceTab.java | 2 +- .../plugins/microbot/util/tabs/Rs2Tab.java | 68 +++------- .../globval/enums/InterfaceTabTest.java | 120 ++++++++++++++++++ 3 files changed, 142 insertions(+), 48 deletions(-) create mode 100644 runelite-client/src/test/java/net/runelite/client/plugins/microbot/globval/enums/InterfaceTabTest.java diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/globval/enums/InterfaceTab.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/globval/enums/InterfaceTab.java index b3b0d6b692..7b9034bb71 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/globval/enums/InterfaceTab.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/globval/enums/InterfaceTab.java @@ -24,7 +24,7 @@ public enum InterfaceTab { SETTINGS("Settings", VarbitID.STONE_OPTIONS1_KEY, 11), MUSIC("Music Player", VarbitID.STONE_MUSIC_KEY, 13), LOGOUT("Logout", VarbitID.STONE_LOGOUT_KEY, 10), - CHAT("Chat Channel", VarbitID.STONE_CLANCHAT_KEY, 11), + CHAT("Chat Channel", VarbitID.STONE_CLANCHAT_KEY, 7), ACC_MAN("Account Management", VarbitID.STONE_ACCOUNT_KEY, 8), EMOTES("Emotes", VarbitID.STONE_OPTIONS2_KEY, 12), // bogus widget info diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/tabs/Rs2Tab.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/tabs/Rs2Tab.java index fa179b446c..f6b62860c6 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/tabs/Rs2Tab.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/tabs/Rs2Tab.java @@ -11,6 +11,10 @@ import net.runelite.client.plugins.microbot.util.keyboard.Rs2Keyboard; import net.runelite.client.plugins.microbot.util.widget.Rs2Widget; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + import static net.runelite.client.plugins.microbot.util.Global.sleepUntil; @Slf4j @@ -18,56 +22,26 @@ public class Rs2Tab { private static final int TAB_SWITCH_SCRIPT = 915; private static volatile InterfaceTab cachedTab = InterfaceTab.NOTHING_SELECTED; + // Derived from InterfaceTab.varcIntIndex — single source of truth. + public static final Map INDEX_TO_TAB; + static { + Map map = new HashMap<>(); + for (InterfaceTab tab : InterfaceTab.values()) { + if (tab.getVarcIntIndex() < 0) continue; + InterfaceTab existing = map.put(tab.getVarcIntIndex(), tab); + if (existing != null) { + throw new ExceptionInInitializerError( + "Duplicate varcIntIndex " + tab.getVarcIntIndex() + + " for " + existing + " and " + tab); + } + } + INDEX_TO_TAB = Collections.unmodifiableMap(map); + } + public static void onVarClientIntChanged(VarClientIntChanged event) { if (event.getIndex() != VarClientID.TOPLEVEL_PANEL) return; int value = Microbot.getClient().getVarcIntValue(VarClientID.TOPLEVEL_PANEL); - switch (value) { - case 0: - cachedTab = InterfaceTab.COMBAT; - break; - case 1: - cachedTab = InterfaceTab.SKILLS; - break; - case 2: - cachedTab = InterfaceTab.QUESTS; - break; - case 3: - cachedTab = InterfaceTab.INVENTORY; - break; - case 4: - cachedTab = InterfaceTab.EQUIPMENT; - break; - case 5: - cachedTab = InterfaceTab.PRAYER; - break; - case 6: - cachedTab = InterfaceTab.MAGIC; - break; - case 7: - cachedTab = InterfaceTab.CHAT; - break; - case 8: - cachedTab = InterfaceTab.ACC_MAN; - break; - case 9: - cachedTab = InterfaceTab.FRIENDS; - break; - case 10: - cachedTab = InterfaceTab.LOGOUT; - break; - case 11: - cachedTab = InterfaceTab.SETTINGS; - break; - case 12: - cachedTab = InterfaceTab.EMOTES; - break; - case 13: - cachedTab = InterfaceTab.MUSIC; - break; - default: - cachedTab = InterfaceTab.NOTHING_SELECTED; - break; - } + cachedTab = INDEX_TO_TAB.getOrDefault(value, InterfaceTab.NOTHING_SELECTED); } public static InterfaceTab getCurrentTab() { diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/microbot/globval/enums/InterfaceTabTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/microbot/globval/enums/InterfaceTabTest.java new file mode 100644 index 0000000000..43a3a47ea7 --- /dev/null +++ b/runelite-client/src/test/java/net/runelite/client/plugins/microbot/globval/enums/InterfaceTabTest.java @@ -0,0 +1,120 @@ +package net.runelite.client.plugins.microbot.globval.enums; + +import net.runelite.client.plugins.microbot.util.tabs.Rs2Tab; +import org.junit.Test; + +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import static org.junit.Assert.*; + +/** + * Validates that every {@link InterfaceTab} has a correct and unique + * {@code varcIntIndex}, and that the {@link Rs2Tab} reverse-lookup map + * stays in sync with the enum. If a game update shifts the indices, + * these tests will catch the drift at CI time. + */ +public class InterfaceTabTest { + + /** All real tabs (everything except the sentinel). */ + private static final Set REAL_TABS = + EnumSet.complementOf(EnumSet.of(InterfaceTab.NOTHING_SELECTED)); + + // ------------------------------------------------------------------ + // varcIntIndex uniqueness & range + // ------------------------------------------------------------------ + + @Test + public void allRealTabsHaveNonNegativeVarcIntIndex() { + for (InterfaceTab tab : REAL_TABS) { + assertTrue(tab.name() + " has negative varcIntIndex " + tab.getVarcIntIndex(), + tab.getVarcIntIndex() >= 0); + } + } + + @Test + public void noTwoTabsShareTheSameVarcIntIndex() { + Map seen = new HashMap<>(); + for (InterfaceTab tab : REAL_TABS) { + InterfaceTab existing = seen.put(tab.getVarcIntIndex(), tab); + assertNull("varcIntIndex " + tab.getVarcIntIndex() + + " is claimed by both " + existing + " and " + tab, + existing); + } + } + + @Test + public void nothingSelectedHasSentinelIndex() { + assertEquals(-1, InterfaceTab.NOTHING_SELECTED.getVarcIntIndex()); + } + + // ------------------------------------------------------------------ + // Completeness: every index in the expected range is covered + // ------------------------------------------------------------------ + + @Test + public void indicesFormContiguousRangeFromZeroToMax() { + int maxIndex = REAL_TABS.stream() + .mapToInt(InterfaceTab::getVarcIntIndex) + .max() + .orElse(-1); + + Set actual = REAL_TABS.stream() + .map(InterfaceTab::getVarcIntIndex) + .collect(Collectors.toSet()); + + for (int i = 0; i <= maxIndex; i++) { + assertTrue("No InterfaceTab maps to varcIntIndex " + i, actual.contains(i)); + } + } + + // ------------------------------------------------------------------ + // Rs2Tab.INDEX_TO_TAB consistency + // ------------------------------------------------------------------ + + @Test + public void rs2TabMapCoversAllRealTabs() { + Map map = Rs2Tab.INDEX_TO_TAB; + for (InterfaceTab tab : REAL_TABS) { + assertSame(tab.name() + " missing from Rs2Tab.INDEX_TO_TAB", + tab, map.get(tab.getVarcIntIndex())); + } + } + + @Test + public void rs2TabMapDoesNotContainSentinel() { + assertFalse("INDEX_TO_TAB should not contain NOTHING_SELECTED", + Rs2Tab.INDEX_TO_TAB.containsValue(InterfaceTab.NOTHING_SELECTED)); + } + + @Test + public void rs2TabMapSizeMatchesRealTabCount() { + assertEquals("INDEX_TO_TAB size should equal real tab count", + REAL_TABS.size(), Rs2Tab.INDEX_TO_TAB.size()); + } + + // ------------------------------------------------------------------ + // Spot-check known values (catches silent renumbering) + // ------------------------------------------------------------------ + + @Test + public void spotCheckKnownVarcIntIndices() { + assertEquals("COMBAT", 0, InterfaceTab.COMBAT.getVarcIntIndex()); + assertEquals("SKILLS", 1, InterfaceTab.SKILLS.getVarcIntIndex()); + assertEquals("QUESTS", 2, InterfaceTab.QUESTS.getVarcIntIndex()); + assertEquals("INVENTORY", 3, InterfaceTab.INVENTORY.getVarcIntIndex()); + assertEquals("EQUIPMENT", 4, InterfaceTab.EQUIPMENT.getVarcIntIndex()); + assertEquals("PRAYER", 5, InterfaceTab.PRAYER.getVarcIntIndex()); + assertEquals("MAGIC", 6, InterfaceTab.MAGIC.getVarcIntIndex()); + assertEquals("CHAT", 7, InterfaceTab.CHAT.getVarcIntIndex()); + assertEquals("ACC_MAN", 8, InterfaceTab.ACC_MAN.getVarcIntIndex()); + assertEquals("FRIENDS", 9, InterfaceTab.FRIENDS.getVarcIntIndex()); + assertEquals("LOGOUT", 10, InterfaceTab.LOGOUT.getVarcIntIndex()); + assertEquals("SETTINGS", 11, InterfaceTab.SETTINGS.getVarcIntIndex()); + assertEquals("EMOTES", 12, InterfaceTab.EMOTES.getVarcIntIndex()); + assertEquals("MUSIC", 13, InterfaceTab.MUSIC.getVarcIntIndex()); + } +} From c7c3e354d24bf5173524df1862e7b180b5e87d6e Mon Sep 17 00:00:00 2001 From: chsami Date: Wed, 15 Apr 2026 13:34:07 +0200 Subject: [PATCH 2/2] build: update microbot version to 2.5.2 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 8905cfbb4b..427dc7fdbb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -31,7 +31,7 @@ project.build.group=net.runelite project.build.version=1.12.24 glslang.path= -microbot.version=2.5.1 +microbot.version=2.5.2 microbot.commit.sha=nogit microbot.repo.url=http://138.201.81.246:8081/repository/microbot-snapshot/ microbot.repo.username=