diff --git a/runelite-client/src/main/java/net/runelite/client/RuneLite.java b/runelite-client/src/main/java/net/runelite/client/RuneLite.java index 130105c1da..a68e51a3b0 100644 --- a/runelite-client/src/main/java/net/runelite/client/RuneLite.java +++ b/runelite-client/src/main/java/net/runelite/client/RuneLite.java @@ -49,6 +49,7 @@ import net.runelite.client.events.SessionClose; import net.runelite.client.events.SessionOpen; import net.runelite.client.menus.MenuManager; import net.runelite.client.plugins.PluginManager; +import net.runelite.client.task.Scheduler; import net.runelite.client.ui.ClientUI; import net.runelite.client.ui.overlay.OverlayRenderer; import net.runelite.http.api.account.AccountClient; @@ -75,7 +76,8 @@ public class RuneLite private final MenuManager menuManager = new MenuManager(this); private OverlayRenderer renderer; private final EventBus eventBus = new EventBus(this::eventExceptionHandler); - private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(4); + private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); + private final Scheduler scheduler = new Scheduler(this); private WSClient wsclient; private AccountSession accountSession; @@ -327,6 +329,11 @@ public class RuneLite return executor; } + public Scheduler getScheduler() + { + return scheduler; + } + public static TrayIcon getTrayIcon() { return trayIcon; diff --git a/runelite-client/src/main/java/net/runelite/client/callback/Hooks.java b/runelite-client/src/main/java/net/runelite/client/callback/Hooks.java index d84951f0ae..d5f688464e 100644 --- a/runelite-client/src/main/java/net/runelite/client/callback/Hooks.java +++ b/runelite-client/src/main/java/net/runelite/client/callback/Hooks.java @@ -32,6 +32,7 @@ import net.runelite.api.Skill; import net.runelite.client.RuneLite; import net.runelite.client.events.*; import net.runelite.client.game.DeathChecker; +import net.runelite.client.task.Scheduler; import net.runelite.client.ui.overlay.OverlayRenderer; import net.runelite.rs.api.MainBufferProvider; import org.slf4j.Logger; @@ -41,16 +42,23 @@ public class Hooks { private static final Logger logger = LoggerFactory.getLogger(Hooks.class); + private static final long CHECK = 600; // ms - how often to run checks + private static final RuneLite runelite = RuneLite.getRunelite(); private static final DeathChecker death = new DeathChecker(runelite); - public static void draw(Object provider, Graphics graphics, int x, int y) - { - // XXX fix injector to use interface in signature - MainBufferProvider mpb = (MainBufferProvider) provider; - BufferedImage image = (BufferedImage) mpb.getImage(); + private static long lastCheck; - OverlayRenderer renderer = runelite.getRenderer(); + public static void clientMainLoop(Object client, boolean arg1) + { + long now = System.currentTimeMillis(); + + if (now - lastCheck < CHECK) + { + return; + } + + lastCheck = now; try { @@ -61,6 +69,18 @@ public class Hooks logger.warn("error during death check", ex); } + Scheduler scheduler = runelite.getScheduler(); + scheduler.tick(); + } + + public static void draw(Object provider, Graphics graphics, int x, int y) + { + // XXX fix injector to use interface in signature + MainBufferProvider mpb = (MainBufferProvider) provider; + BufferedImage image = (BufferedImage) mpb.getImage(); + + OverlayRenderer renderer = runelite.getRenderer(); + try { renderer.render(image); 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 3d2fce1e5e..7d22cd2eb7 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 @@ -24,9 +24,11 @@ */ package net.runelite.client.plugins; +import net.runelite.client.task.ScheduledMethod; import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.Service; import com.google.common.util.concurrent.ServiceManager; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -56,6 +58,7 @@ import net.runelite.client.plugins.xpglobes.XpGlobes; import net.runelite.client.plugins.xptracker.XPTracker; import net.runelite.client.plugins.xtea.Xtea; import net.runelite.client.plugins.zulrah.Zulrah; +import net.runelite.client.task.Schedule; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -117,6 +120,8 @@ public class PluginManager { logger.debug("Plugin {} is now running", plugin); runelite.getEventBus().register(plugin); + + schedule(plugin); } @Override @@ -124,6 +129,7 @@ public class PluginManager { logger.debug("Plugin {} is stopping", plugin); runelite.getEventBus().unregister(plugin); + unschedule(plugin); } @Override @@ -134,6 +140,7 @@ public class PluginManager if (from == Service.State.RUNNING) { runelite.getEventBus().unregister(plugin); + unschedule(plugin); } } }; @@ -154,4 +161,38 @@ public class PluginManager .map(s -> (Plugin) s) .collect(Collectors.toList()); } + + private void schedule(Plugin plugin) + { + for (Method method : plugin.getClass().getMethods()) + { + Schedule schedule = method.getAnnotation(Schedule.class); + + if (schedule == null) + { + continue; + } + + ScheduledMethod scheduledMethod = new ScheduledMethod(schedule, method, plugin); + logger.debug("Scheduled task {}", scheduledMethod); + + runelite.getScheduler().addScheduledMethod(scheduledMethod); + } + } + + private void unschedule(Plugin plugin) + { + List methods = new ArrayList<>(runelite.getScheduler().getScheduledMethods()); + + for (ScheduledMethod method : methods) + { + if (method.getObject() != plugin) + { + continue; + } + + logger.debug("Removing scheduled task {}", method); + runelite.getScheduler().removeScheduledMethod(method); + } + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/clanchat/ClanChat.java b/runelite-client/src/main/java/net/runelite/client/plugins/clanchat/ClanChat.java index 5b9f8d780c..b372428c4f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/clanchat/ClanChat.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/clanchat/ClanChat.java @@ -24,35 +24,31 @@ */ package net.runelite.client.plugins.clanchat; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; +import java.time.temporal.ChronoUnit; import net.runelite.api.GameState; import net.runelite.api.widgets.Widget; import net.runelite.api.widgets.WidgetInfo; import net.runelite.client.RuneLite; import net.runelite.client.plugins.Plugin; +import net.runelite.client.task.Schedule; public class ClanChat extends Plugin { - - private final long INTERVAL = 600; - private ScheduledFuture future; - @Override protected void startUp() throws Exception { - ScheduledExecutorService executor = RuneLite.getRunelite().getExecutor(); - future = executor.scheduleAtFixedRate(this::updateClanChatTitle, INTERVAL, INTERVAL, TimeUnit.MILLISECONDS); } @Override protected void shutDown() throws Exception { - future.cancel(true); } - private void updateClanChatTitle() + @Schedule( + period = 600, + unit = ChronoUnit.MILLIS + ) + public void updateClanChatTitle() { if (RuneLite.getClient().getGameState() != GameState.LOGGED_IN) { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/combatnotifier/CombatNotifier.java b/runelite-client/src/main/java/net/runelite/client/plugins/combatnotifier/CombatNotifier.java index 876e1fc425..e366e2047a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/combatnotifier/CombatNotifier.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/combatnotifier/CombatNotifier.java @@ -24,49 +24,41 @@ */ package net.runelite.client.plugins.combatnotifier; +import java.time.Duration; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import net.runelite.api.Actor; import net.runelite.api.Client; import net.runelite.api.GameState; import net.runelite.api.Player; import net.runelite.client.RuneLite; import net.runelite.client.plugins.Plugin; - -import java.awt.*; -import java.time.Duration; -import java.time.Instant; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - +import net.runelite.client.task.Schedule; public class CombatNotifier extends Plugin { - private static final int CHECK_INTERVAL = 1; - private final Client client = RuneLite.getClient(); private final RuneLite runelite = RuneLite.getRunelite(); private final CombatNotifierConfig config = runelite.getConfigManager() .getConfig(CombatNotifierConfig.class); - private ScheduledFuture future; private Instant lastInteracting; @Override protected void startUp() throws Exception { - ScheduledExecutorService executor = RuneLite.getRunelite().getExecutor(); - future = executor.scheduleAtFixedRate(this::checkIdle, CHECK_INTERVAL, CHECK_INTERVAL, TimeUnit.SECONDS); } @Override protected void shutDown() throws Exception { - future.cancel(true); } - private void checkIdle() + @Schedule( + period = 1, + unit = ChronoUnit.SECONDS + ) + public void checkIdle() { if (client.getGameState() != GameState.LOGGED_IN || !config.isEnabled()) { @@ -88,4 +80,4 @@ public class CombatNotifier extends Plugin } } -} \ No newline at end of file +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingPlugin.java index 1d1736450a..78c3b839da 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingPlugin.java @@ -25,32 +25,27 @@ package net.runelite.client.plugins.fishing; import com.google.common.eventbus.Subscribe; +import java.time.Duration; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.Arrays; +import java.util.Collection; import net.runelite.api.ChatMessageType; import net.runelite.client.RuneLite; import net.runelite.client.events.ChatMessage; import net.runelite.client.events.ConfigChanged; import net.runelite.client.plugins.Plugin; +import net.runelite.client.task.Schedule; import net.runelite.client.ui.overlay.Overlay; -import java.time.Duration; -import java.time.Instant; -import java.util.Arrays; -import java.util.Collection; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; - public class FishingPlugin extends Plugin { - private static final int CHECK_INTERVAL = 1; - private final RuneLite runelite = RuneLite.getRunelite(); private final FishingConfig config = runelite.getConfigManager().getConfig(FishingConfig.class); private final FishingOverlay overlay = new FishingOverlay(this); private final FishingSpotOverlay spotOverlay = new FishingSpotOverlay(this); private FishingSession session = new FishingSession(); - private ScheduledFuture future; @Override public Collection getOverlays() @@ -61,9 +56,6 @@ public class FishingPlugin extends Plugin @Override protected void startUp() throws Exception { - ScheduledExecutorService executor = runelite.getExecutor(); - future = executor.scheduleAtFixedRate(this::checkFishing, CHECK_INTERVAL, CHECK_INTERVAL, TimeUnit.SECONDS); - // Initialize overlay config spotOverlay.updateConfig(); } @@ -71,7 +63,6 @@ public class FishingPlugin extends Plugin @Override protected void shutDown() throws Exception { - future.cancel(true); } public FishingConfig getConfig() @@ -104,7 +95,11 @@ public class FishingPlugin extends Plugin spotOverlay.updateConfig(); } - private void checkFishing() + @Schedule( + period = 1, + unit = ChronoUnit.SECONDS + ) + public void checkFishing() { Instant lastFishCaught = session.getLastFishCaught(); if (lastFishCaught == null) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifier.java b/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifier.java index 5dd8b725e9..e3f2565b13 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifier.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifier.java @@ -25,13 +25,9 @@ package net.runelite.client.plugins.idlenotifier; import com.google.common.eventbus.Subscribe; -import java.awt.Toolkit; -import java.awt.TrayIcon; import java.time.Duration; import java.time.Instant; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; +import java.time.temporal.ChronoUnit; import static net.runelite.api.AnimationID.*; import net.runelite.api.Client; import net.runelite.api.GameState; @@ -39,32 +35,26 @@ import net.runelite.api.Player; import net.runelite.client.RuneLite; import net.runelite.client.events.AnimationChanged; import net.runelite.client.plugins.Plugin; +import net.runelite.client.task.Schedule; public class IdleNotifier extends Plugin { - private static final String OPERATING_SYSTEM = System.getProperty("os.name"); - private static final int CHECK_INTERVAL = 2; private static final Duration WAIT_DURATION = Duration.ofMillis(2500L); private final Client client = RuneLite.getClient(); private final RuneLite runelite = RuneLite.getRunelite(); - private final TrayIcon trayIcon = RuneLite.getTrayIcon(); - private ScheduledFuture future; private Instant lastAnimating; private boolean notifyIdle = false; @Override protected void startUp() throws Exception { - ScheduledExecutorService executor = runelite.getExecutor(); - future = executor.scheduleAtFixedRate(this::checkIdle, CHECK_INTERVAL, CHECK_INTERVAL, TimeUnit.SECONDS); } @Override protected void shutDown() throws Exception { - future.cancel(true); } @Subscribe @@ -74,6 +64,7 @@ public class IdleNotifier extends Plugin { return; } + int animation = client.getLocalPlayer().getAnimation(); switch (animation) { @@ -148,7 +139,11 @@ public class IdleNotifier extends Plugin } } - private void checkIdle() + @Schedule( + period = 2, + unit = ChronoUnit.SECONDS + ) + public void checkIdle() { Player local = client.getLocalPlayer(); if (notifyIdle && local.getAnimation() == IDLE diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/woodcutting/WoodcuttingPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/woodcutting/WoodcuttingPlugin.java index 261a44932d..c3a8e94785 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/woodcutting/WoodcuttingPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/woodcutting/WoodcuttingPlugin.java @@ -27,25 +27,21 @@ package net.runelite.client.plugins.woodcutting; import com.google.common.eventbus.Subscribe; import java.time.Duration; import java.time.Instant; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; +import java.time.temporal.ChronoUnit; import net.runelite.api.ChatMessageType; import net.runelite.client.RuneLite; import net.runelite.client.events.ChatMessage; import net.runelite.client.plugins.Plugin; +import net.runelite.client.task.Schedule; import net.runelite.client.ui.overlay.Overlay; public class WoodcuttingPlugin extends Plugin { - private static final int CHECK_INTERVAL = 1; - private final RuneLite runelite = RuneLite.getRunelite(); private final WoodcuttingConfig config = runelite.getConfigManager().getConfig(WoodcuttingConfig.class); private final WoodcuttingOverlay overlay = new WoodcuttingOverlay(this); private WoodcuttingSession session = new WoodcuttingSession(); - private ScheduledFuture future; @Override public Overlay getOverlay() @@ -56,14 +52,11 @@ public class WoodcuttingPlugin extends Plugin @Override protected void startUp() throws Exception { - ScheduledExecutorService executor = runelite.getExecutor(); - future = executor.scheduleAtFixedRate(this::checkCutting, CHECK_INTERVAL, CHECK_INTERVAL, TimeUnit.SECONDS); } @Override protected void shutDown() throws Exception { - future.cancel(true); } public WoodcuttingConfig getConfig() @@ -90,7 +83,11 @@ public class WoodcuttingPlugin extends Plugin } } - private void checkCutting() + @Schedule( + period = 1, + unit = ChronoUnit.SECONDS + ) + public void checkCutting() { Instant lastLogCut = session.getLastLogCut(); if (lastLogCut == null) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XPTracker.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XPTracker.java index 3770715896..7ca55b7190 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XPTracker.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XPTracker.java @@ -27,7 +27,6 @@ package net.runelite.client.plugins.xptracker; import com.google.common.eventbus.Subscribe; import net.runelite.api.Client; import net.runelite.api.GameState; -import net.runelite.api.Player; import net.runelite.api.Skill; import net.runelite.client.RuneLite; import net.runelite.client.events.ExperienceChanged; @@ -35,13 +34,11 @@ import net.runelite.client.events.GameStateChanged; import net.runelite.client.plugins.Plugin; import net.runelite.client.ui.ClientUI; import net.runelite.client.ui.NavigationButton; -import net.runelite.client.util.RunnableExceptionLogger; import java.awt.Font; import java.awt.GraphicsEnvironment; import java.awt.event.ActionEvent; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; +import java.time.temporal.ChronoUnit; +import net.runelite.client.task.Schedule; public class XPTracker extends Plugin { @@ -50,25 +47,25 @@ public class XPTracker extends Plugin private final RuneLite runeLite = RuneLite.getRunelite(); private final ClientUI ui = runeLite.getGui(); private final Client client = RuneLite.getClient(); - private ScheduledFuture future; private final NavigationButton navButton = new NavigationButton("XP Tracker"); private final XPPanel xpPanel = new XPPanel(runeLite, this); - private SkillXPInfo[] xpInfos = new SkillXPInfo[NUMBER_OF_SKILLS]; + private final SkillXPInfo[] xpInfos = new SkillXPInfo[NUMBER_OF_SKILLS]; @Subscribe public void onGameStateChanged(GameStateChanged event) { //reset upon login if (event.getGameState() == GameState.LOGGED_IN) + { xpPanel.resetAllSkillXpHr(); + } } @Subscribe public void onXpChanged(ExperienceChanged event) { Skill skill = event.getSkill(); - String currentUser = client.getLocalPlayer().getName(); int skillIdx = skill.ordinal(); //To catch login ExperienceChanged event. @@ -79,7 +76,7 @@ public class XPTracker extends Plugin else { xpInfos[skillIdx] = new SkillXPInfo(client.getSkillExperience(skill), - skill); + skill); } } @@ -96,10 +93,6 @@ public class XPTracker extends Plugin navButton.getButton().setText("XP"); ui.getNavigationPanel().addNavigation(navButton); - ScheduledExecutorService executor = RuneLite.getRunelite().getExecutor(); - future = executor.scheduleAtFixedRate(RunnableExceptionLogger.wrap( - xpPanel::updateAllSkillXpHr), 0, 600, TimeUnit.MILLISECONDS); - Font font = Font.createFont(Font.TRUETYPE_FONT, getClass().getResourceAsStream("/runescape.ttf")); font = font.deriveFont(Font.BOLD, 16); GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); @@ -109,7 +102,15 @@ public class XPTracker extends Plugin @Override protected void shutDown() throws Exception { - future.cancel(true); + } + + @Schedule( + period = 600, + unit = ChronoUnit.MILLIS + ) + public void updateXp() + { + xpPanel.updateAllSkillXpHr(); } public SkillXPInfo[] getXpInfos() diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/StatusOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/StatusOverlay.java index 1c73ab8ba9..6160d714ea 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/StatusOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/StatusOverlay.java @@ -55,32 +55,27 @@ class StatusOverlay extends Overlay @Override public Dimension render(Graphics2D graphics) { - ZulrahInstance current, next; + Fight fight = plugin.getFight(); - synchronized (plugin) + if (client.getGameState() != GameState.LOGGED_IN || fight == null) { - Fight fight = plugin.getFight(); - - if (client.getGameState() != GameState.LOGGED_IN || fight == null) - { - return null; - } - - //TODO: Add prayer checking and health warning - graphics.setColor(Color.WHITE); - - ZulrahPattern pattern = fight.getPattern(); - if (pattern == null) - { - // can draw at least the starting place here? - return null; - } - - // Show current type, next type, and jad - current = fight.getZulrah(); - next = pattern.get(fight.getStage() + 1); + return null; } + //TODO: Add prayer checking and health warning + graphics.setColor(Color.WHITE); + + ZulrahPattern pattern = fight.getPattern(); + if (pattern == null) + { + // can draw at least the starting place here? + return null; + } + + // Show current type, next type, and jad + ZulrahInstance current = fight.getZulrah(); + ZulrahInstance next = pattern.get(fight.getStage() + 1); + String currentStr = "Current: " + current.getType(); String nextStr = "Next: " + (next != null ? next.getType() : "Restart"); String jadStr; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/TileOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/TileOverlay.java index c638da06a5..375da64e4b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/TileOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/TileOverlay.java @@ -25,7 +25,6 @@ */ package net.runelite.client.plugins.zulrah; -import java.awt.BasicStroke; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics2D; @@ -54,29 +53,22 @@ public class TileOverlay extends Overlay @Override public Dimension render(Graphics2D graphics) { - ZulrahPattern pattern; - Point startLocationWorld; - int stage; + Fight fight = plugin.getFight(); - synchronized (plugin) + if (client.getGameState() != GameState.LOGGED_IN || fight == null) { - Fight fight = plugin.getFight(); - - if (client.getGameState() != GameState.LOGGED_IN || fight == null) - { - return null; - } - - pattern = fight.getPattern(); - if (pattern == null) - { - return null; - } - - startLocationWorld = fight.getStartLocationWorld(); - stage = fight.getStage(); + return null; } + ZulrahPattern pattern = fight.getPattern(); + if (pattern == null) + { + return null; + } + + Point startLocationWorld = fight.getStartLocationWorld(); + int stage = fight.getStage(); + ZulrahInstance current = pattern.get(stage); if (current == null) { @@ -123,7 +115,7 @@ public class TileOverlay extends Overlay //to make the centre of the tile on the point, rather than the tile the point resides in localTile = new Point(localTile.getX() + Perspective.LOCAL_TILE_SIZE / 2, localTile.getY() + Perspective.LOCAL_TILE_SIZE / 2); - + Polygon poly = Perspective.getCanvasTilePoly(client, localTile); if (poly != null) { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/Zulrah.java b/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/Zulrah.java index 9e9e9833a1..9f8125f685 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/Zulrah.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/Zulrah.java @@ -25,11 +25,9 @@ */ package net.runelite.client.plugins.zulrah; +import java.time.temporal.ChronoUnit; import java.util.Arrays; import java.util.Collection; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; import net.runelite.api.Client; import net.runelite.api.GameState; import net.runelite.api.NPC; @@ -44,8 +42,8 @@ import net.runelite.client.plugins.zulrah.patterns.ZulrahPatternA; import net.runelite.client.plugins.zulrah.patterns.ZulrahPatternB; import net.runelite.client.plugins.zulrah.patterns.ZulrahPatternC; import net.runelite.client.plugins.zulrah.patterns.ZulrahPatternD; +import net.runelite.client.task.Schedule; import net.runelite.client.ui.overlay.Overlay; -import net.runelite.client.util.RunnableExceptionLogger; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -68,8 +66,6 @@ public class Zulrah extends Plugin private Fight fight; - private ScheduledFuture future; - @Override public Collection getOverlays() { @@ -79,17 +75,18 @@ public class Zulrah extends Plugin @Override protected void startUp() throws Exception { - ScheduledExecutorService executor = RuneLite.getRunelite().getExecutor(); - future = executor.scheduleAtFixedRate(RunnableExceptionLogger.wrap(this::update), 100, 100, TimeUnit.MILLISECONDS); } @Override protected void shutDown() throws Exception { - future.cancel(true); } - private synchronized void update() + @Schedule( + period = 600, + unit = ChronoUnit.MILLIS + ) + public void update() { if (client == null || client.getGameState() != GameState.LOGGED_IN) { diff --git a/runelite-client/src/main/java/net/runelite/client/task/Schedule.java b/runelite-client/src/main/java/net/runelite/client/task/Schedule.java new file mode 100644 index 0000000000..14c78dd527 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/task/Schedule.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2017, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.task; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.time.temporal.ChronoUnit; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface Schedule +{ + long period(); + + ChronoUnit unit(); + + boolean asynchronous() default false; +} diff --git a/runelite-client/src/main/java/net/runelite/client/task/ScheduledMethod.java b/runelite-client/src/main/java/net/runelite/client/task/ScheduledMethod.java new file mode 100644 index 0000000000..e91a5a89ff --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/task/ScheduledMethod.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2017, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.task; + +import java.lang.reflect.Method; +import java.time.Instant; + +public class ScheduledMethod +{ + private final Schedule schedule; + private final Method method; + private final Object object; + private Instant last = Instant.now(); + + public ScheduledMethod(Schedule schedule, Method method, Object object) + { + this.schedule = schedule; + this.method = method; + this.object = object; + } + + @Override + public String toString() + { + return "ScheduledMethod{" + "schedule=" + schedule + ", method=" + method + ", object=" + object + '}'; + } + + public Schedule getSchedule() + { + return schedule; + } + + public Method getMethod() + { + return method; + } + + public Object getObject() + { + return object; + } + + public Instant getLast() + { + return last; + } + + public void setLast(Instant last) + { + this.last = last; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/task/Scheduler.java b/runelite-client/src/main/java/net/runelite/client/task/Scheduler.java new file mode 100644 index 0000000000..ae09732940 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/task/Scheduler.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2017, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.task; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ScheduledExecutorService; +import net.runelite.client.RuneLite; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Scheduler +{ + private static final Logger logger = LoggerFactory.getLogger(Scheduler.class); + + private final RuneLite runelite; + private final List scheduledMethods = new ArrayList<>(); + + public Scheduler(RuneLite runelite) + { + this.runelite = runelite; + } + + public void addScheduledMethod(ScheduledMethod method) + { + scheduledMethods.add(method); + } + + public void removeScheduledMethod(ScheduledMethod method) + { + scheduledMethods.remove(method); + } + + public List getScheduledMethods() + { + return Collections.unmodifiableList(scheduledMethods); + } + + public void tick() + { + Instant now = Instant.now(); + + for (ScheduledMethod scheduledMethod : scheduledMethods) + { + Instant last = scheduledMethod.getLast(); + + Duration difference = Duration.between(last, now); + + Schedule schedule = scheduledMethod.getSchedule(); + Duration timeSinceRun = Duration.of(schedule.period(), schedule.unit()); + + if (difference.compareTo(timeSinceRun) > 0) + { + logger.trace("Scheduled task triggered: {}", scheduledMethod); + + scheduledMethod.setLast(now); + + if (schedule.asynchronous()) + { + ScheduledExecutorService executor = runelite.getExecutor(); + executor.submit(() -> run(scheduledMethod)); + } + else + { + run(scheduledMethod); + } + } + } + } + + private void run(ScheduledMethod scheduledMethod) + { + Method method = scheduledMethod.getMethod(); + + try + { + method.invoke(scheduledMethod.getObject()); + } + catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) + { + logger.warn("error invoking scheduled task", ex); + } + catch (Exception ex) + { + logger.warn("error during scheduled task", ex); + } + } +}