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/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..51b18fe2fd6 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/MicrobotVersionChecker.java @@ -0,0 +1,142 @@ +package net.runelite.client.plugins.microbot; + +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.ScheduledFuture; +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; + +@Slf4j +@Singleton +public class MicrobotVersionChecker +{ + private final AtomicBoolean newVersionAvailable = new AtomicBoolean(false); + 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; + } + + try + { + String remoteVersion = fetchRemoteVersion(); + String localVersion = RuneLiteProperties.getMicrobotVersion(); + 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(remote, local); + } + else + { + log.debug("Microbshot client is up to date: {}", local); + } + } + catch (Exception e) + { + log.warn("Could not check Microbot client version", e); + } + } + + private String fetchRemoteVersion() throws Exception + { + 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 + { + 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 + { + var frame = ClientUI.getFrame(); + if (frame == null) + { + return; + } + String oldTitle = String.valueOf(frame.getTitle()); + if (!oldTitle.contains(NEW_CLIENT_MARKER)) + { + frame.setTitle(oldTitle + " " + NEW_CLIENT_MARKER); + } + } + catch (Exception e) + { + log.warn("Failed to update client title", e); + } + }); + } + + public void checkForUpdate() + { + if (scheduled.compareAndSet(false, true)) + { + future = scheduledExecutorService.scheduleWithFixedDelay(this::runVersionCheck, 0, 10, TimeUnit.MINUTES); + } + } + + public void shutdown() + { + try + { + if (future != null) + { + future.cancel(true); + } + } + finally + { + scheduledExecutorService.shutdownNow(); + newVersionAvailable.set(false); + scheduled.set(false); + } + } +} 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());