From 8cc72fc48dd949d469a18d52aba23a93d65b8431 Mon Sep 17 00:00:00 2001 From: g-mason0 <19415334+g-mason0@users.noreply.github.com> Date: Wed, 20 Aug 2025 16:06:37 -0400 Subject: [PATCH 1/4] feat(microbot): add version checker that will add text into the title bar if the client version is out of date --- .../plugins/microbot/MicrobotPlugin.java | 6 ++ .../microbot/MicrobotVersionChecker.java | 99 +++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/MicrobotVersionChecker.java diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/MicrobotPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/MicrobotPlugin.java index 4cd1f16be99..ea934737b47 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/MicrobotPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/MicrobotPlugin.java @@ -110,6 +110,9 @@ MicrobotConfig provideConfig(ConfigManager configManager) @Inject private EventBus eventBus; private GameChatAppender gameChatAppender; + + @Inject + private MicrobotVersionChecker microbotVersionChecker; // Widget change tracking for overlay cache invalidation private volatile boolean widgetLayoutChanged = false; @@ -125,6 +128,8 @@ protected void startUp() throws AWTException log.info("Microbot: {} - {}", RuneLiteProperties.getMicrobotVersion(), RuneLiteProperties.getMicrobotCommit()); log.info("JVM: {} {}", System.getProperty("java.vendor"), System.getProperty("java.runtime.version")); + microbotVersionChecker.checkForUpdate(); + gameChatAppender = new GameChatAppender(); gameChatAppender.setName("GAME_CHAT"); @@ -195,6 +200,7 @@ protected void shutDown() microbotOverlay.cacheButton.unhookMouseListener(); clientToolbar.removeNavigation(navButton); if (gameChatAppender.isStarted()) gameChatAppender.stop(); + microbotVersionChecker.shutdown(); // Shutdown the cache system shutdownCacheSystem(); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/MicrobotVersionChecker.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/MicrobotVersionChecker.java new file mode 100644 index 00000000000..2cc34e4ba44 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/MicrobotVersionChecker.java @@ -0,0 +1,99 @@ +package net.runelite.client.plugins.microbot; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import javax.inject.Singleton; +import javax.swing.SwingUtilities; +import lombok.extern.slf4j.Slf4j; +import net.runelite.client.RuneLiteProperties; +import net.runelite.client.ui.ClientUI; +import org.jetbrains.annotations.NotNull; + +@Slf4j +@Singleton +public class MicrobotVersionChecker +{ + private final AtomicBoolean newVersionAvailable = new AtomicBoolean(false); + private static final String REMOTE_VERSION_URL = "https://microbot.cloud/api/version/client"; + private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() + { + @Override + public Thread newThread(@NotNull Runnable r) + { + Thread t = new Thread(r); + t.setName(this.getClass().getSimpleName()); + return t; + } + }); + + private void runVersionCheck() + { + if (newVersionAvailable.get()) + { + return; + } + + try + { + String remoteVersion = fetchRemoteVersion(); + String localVersion = RuneLiteProperties.getMicrobotVersion(); + if (remoteVersion != null && !remoteVersion.trim().equals(localVersion)) + { + newVersionAvailable.set(true); + notifyNewVersionAvailable(remoteVersion, localVersion); + } + else + { + log.debug("Microbot client is up to date: {}", localVersion); + } + } + catch (Exception e) + { + log.warn("Could not check Microbot client version", e); + } + } + + private String fetchRemoteVersion() throws Exception + { + URL url = new URL(REMOTE_VERSION_URL); + try (BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()))) + { + return in.readLine(); + } + } + + private void notifyNewVersionAvailable(String remoteVersion, String localVersion) + { + SwingUtilities.invokeLater(() -> { + try + { + String oldTitle = ClientUI.getFrame().getTitle(); + if (!oldTitle.contains("(NEW CLIENT AVAILABLE)")) + { + log.info("New Microbot client version available: {} (current: {})", remoteVersion, localVersion); + ClientUI.getFrame().setTitle(oldTitle + " (NEW CLIENT AVAILABLE)"); + } + } + catch (Exception e) + { + log.warn("Failed to update client title", e); + } + }); + } + + public void checkForUpdate() + { + scheduledExecutorService.scheduleWithFixedDelay(this::runVersionCheck, 0, 10, TimeUnit.MINUTES); + } + + public void shutdown() { + scheduledExecutorService.shutdownNow(); + newVersionAvailable.set(false); + } +} From d392740b1e862f5d32442bc27ade3f1d351ed85d Mon Sep 17 00:00:00 2001 From: g-mason0 <19415334+g-mason0@users.noreply.github.com> Date: Wed, 20 Aug 2025 16:09:07 -0400 Subject: [PATCH 2/4] chore(pluginmanager): remove devmode for sideloaded plugins directory --- .../java/net/runelite/client/plugins/PluginManager.java | 7 ------- .../net/runelite/client/plugins/PluginManagerTest.java | 4 ++-- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java index 79db99736b6..22075df864f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java @@ -76,7 +76,6 @@ public class PluginManager { private static final String PLUGIN_PACKAGE = "net.runelite.client.plugins"; private static final File SIDELOADED_PLUGINS = new File(RuneLite.RUNELITE_DIR, "sideloaded-plugins"); - private final boolean developerMode; private final boolean safeMode; private final EventBus eventBus; private final Scheduler scheduler; @@ -93,13 +92,11 @@ public void addPlugin(Plugin plugin) { @Inject @VisibleForTesting PluginManager( - @Named("developerMode") final boolean developerMode, @Named("safeMode") final boolean safeMode, final EventBus eventBus, final Scheduler scheduler, final ConfigManager configManager, final Provider sceneTileManager) { - this.developerMode = developerMode; this.safeMode = safeMode; this.eventBus = eventBus; this.scheduler = scheduler; @@ -274,10 +271,6 @@ public void loadRuneliteCorePlugins() throws IOException, PluginInstantiationExc } public void loadSideLoadPlugins() { - if (!developerMode) { - return; - } - File[] files = SIDELOADED_PLUGINS.listFiles(); if (files == null) { return; diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/PluginManagerTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/PluginManagerTest.java index 5e463a871f8..c31b2718d95 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/PluginManagerTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/PluginManagerTest.java @@ -139,7 +139,7 @@ public void before() throws IOException @Test public void testLoadPlugins() throws Exception { - var pluginManager = new PluginManager(false, false, null, null, null, null); + var pluginManager = new PluginManager(false, null, null, null, null); pluginManager.loadCorePlugins(); var plugins = pluginManager.getPlugins(); @@ -161,7 +161,7 @@ public void testLoadPlugins() throws Exception @Ignore public void dumpGraph() throws Exception { - PluginManager pluginManager = new PluginManager(true, false, null, null, null, null); + PluginManager pluginManager = new PluginManager(false, null, null, null, null); pluginManager.loadCorePlugins(); Injector graphvizInjector = Guice.createInjector(new GraphvizModule()); From d88fd69600cd3c101fa3af31dfb9242d478e78c2 Mon Sep 17 00:00:00 2001 From: g-mason0 <19415334+g-mason0@users.noreply.github.com> Date: Wed, 20 Aug 2025 19:09:29 -0400 Subject: [PATCH 3/4] chore(microbot): changes to VersionChecker based on review --- .../microbot/MicrobotVersionChecker.java | 80 +++++++++++++------ 1 file changed, 56 insertions(+), 24 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/MicrobotVersionChecker.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/MicrobotVersionChecker.java index 2cc34e4ba44..4ad4746de1a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/MicrobotVersionChecker.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/MicrobotVersionChecker.java @@ -2,10 +2,11 @@ import java.io.BufferedReader; import java.io.InputStreamReader; +import java.net.HttpURLConnection; import java.net.URL; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import javax.inject.Singleton; @@ -13,29 +14,29 @@ import lombok.extern.slf4j.Slf4j; import net.runelite.client.RuneLiteProperties; import net.runelite.client.ui.ClientUI; -import org.jetbrains.annotations.NotNull; @Slf4j @Singleton public class MicrobotVersionChecker { private final AtomicBoolean newVersionAvailable = new AtomicBoolean(false); - private static final String REMOTE_VERSION_URL = "https://microbot.cloud/api/version/client"; - private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() - { - @Override - public Thread newThread(@NotNull Runnable r) - { - Thread t = new Thread(r); - t.setName(this.getClass().getSimpleName()); - return t; - } + private final AtomicBoolean scheduled = new AtomicBoolean(false); + private volatile ScheduledFuture future; + private final String REMOTE_VERSION_URL = "https://microbot.cloud/api/version/client"; + private static final String NEW_CLIENT_MARKER = "(NEW CLIENT AVAILABLE)"; + + private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(r -> { + Thread t = new Thread(r, MicrobotVersionChecker.class.getSimpleName()); + t.setDaemon(true); + t.setUncaughtExceptionHandler((th, e) -> log.warn("Version checker thread error", e)); + return t; }); private void runVersionCheck() { if (newVersionAvailable.get()) { + appendToTitle(); return; } @@ -43,14 +44,16 @@ private void runVersionCheck() { String remoteVersion = fetchRemoteVersion(); String localVersion = RuneLiteProperties.getMicrobotVersion(); - if (remoteVersion != null && !remoteVersion.trim().equals(localVersion)) + String remote = remoteVersion == null ? null : remoteVersion.trim(); + String local = localVersion == null ? "" : localVersion.trim(); + if (remote != null && !remote.isEmpty() && !remote.equals(local)) { newVersionAvailable.set(true); - notifyNewVersionAvailable(remoteVersion, localVersion); + notifyNewVersionAvailable(remote, local); } else { - log.debug("Microbot client is up to date: {}", localVersion); + log.debug("Microbot client is up to date: {}", local); } } catch (Exception e) @@ -61,23 +64,48 @@ private void runVersionCheck() private String fetchRemoteVersion() throws Exception { - URL url = new URL(REMOTE_VERSION_URL); - try (BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()))) + var url = new URL(REMOTE_VERSION_URL); + var conn = (HttpURLConnection) url.openConnection(); + conn.setConnectTimeout(5_000); + conn.setReadTimeout(5_000); + conn.setRequestMethod("GET"); + conn.setInstanceFollowRedirects(true); + try (var reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), java.nio.charset.StandardCharsets.UTF_8))) + { + if (conn.getResponseCode() != 200) + { + log.debug("Version check responded with HTTP {}", conn.getResponseCode()); + return null; + } + String line = reader.readLine(); + return line != null ? line.trim() : null; + } + finally { - return in.readLine(); + conn.disconnect(); } } private void notifyNewVersionAvailable(String remoteVersion, String localVersion) + { + appendToTitle(); + log.info("New Microbot client version available: {} (current: {})", remoteVersion, localVersion); + } + + private void appendToTitle() { SwingUtilities.invokeLater(() -> { try { - String oldTitle = ClientUI.getFrame().getTitle(); - if (!oldTitle.contains("(NEW CLIENT AVAILABLE)")) + var frame = ClientUI.getFrame(); + if (frame == null) + { + return; + } + String oldTitle = String.valueOf(frame.getTitle()); + if (!oldTitle.contains(NEW_CLIENT_MARKER)) { - log.info("New Microbot client version available: {} (current: {})", remoteVersion, localVersion); - ClientUI.getFrame().setTitle(oldTitle + " (NEW CLIENT AVAILABLE)"); + frame.setTitle(oldTitle + " " + NEW_CLIENT_MARKER); } } catch (Exception e) @@ -89,10 +117,14 @@ private void notifyNewVersionAvailable(String remoteVersion, String localVersion public void checkForUpdate() { - scheduledExecutorService.scheduleWithFixedDelay(this::runVersionCheck, 0, 10, TimeUnit.MINUTES); + if (scheduled.compareAndSet(false, true)) + { + future = scheduledExecutorService.scheduleWithFixedDelay(this::runVersionCheck, 0, 10, TimeUnit.MINUTES); + } } - public void shutdown() { + public void shutdown() + { scheduledExecutorService.shutdownNow(); newVersionAvailable.set(false); } From 29004ffd5a1ca163c20d7c2ce36a53ae5212ee8a Mon Sep 17 00:00:00 2001 From: g-mason0 <19415334+g-mason0@users.noreply.github.com> Date: Wed, 20 Aug 2025 19:14:21 -0400 Subject: [PATCH 4/4] chore(microbot): properly handle shutdown in VersionChecker --- .../microbot/MicrobotVersionChecker.java | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/MicrobotVersionChecker.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/MicrobotVersionChecker.java index 4ad4746de1a..51b18fe2fd6 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/MicrobotVersionChecker.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/MicrobotVersionChecker.java @@ -45,7 +45,7 @@ private void runVersionCheck() String remoteVersion = fetchRemoteVersion(); String localVersion = RuneLiteProperties.getMicrobotVersion(); String remote = remoteVersion == null ? null : remoteVersion.trim(); - String local = localVersion == null ? "" : localVersion.trim(); + String local = localVersion == null ? "" : localVersion.trim(); if (remote != null && !remote.isEmpty() && !remote.equals(local)) { newVersionAvailable.set(true); @@ -53,7 +53,7 @@ private void runVersionCheck() } else { - log.debug("Microbot client is up to date: {}", local); + log.debug("Microbshot client is up to date: {}", local); } } catch (Exception e) @@ -125,7 +125,18 @@ public void checkForUpdate() public void shutdown() { - scheduledExecutorService.shutdownNow(); - newVersionAvailable.set(false); + try + { + if (future != null) + { + future.cancel(true); + } + } + finally + { + scheduledExecutorService.shutdownNow(); + newVersionAvailable.set(false); + scheduled.set(false); + } } }