Skip to content

Commit b6c1391

Browse files
authored
Merge pull request #1654 from chsami/development
Update breakhandler to allow to stop a plugin when taking a break
2 parents e1d8d5d + 6fc0a76 commit b6c1391

File tree

8 files changed

+506
-50
lines changed

8 files changed

+506
-50
lines changed

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ project.build.group=net.runelite
3131
project.build.version=1.12.12.1
3232

3333
glslang.path=
34-
microbot.version=2.1.11
34+
microbot.version=2.1.12
3535
microbot.commit.sha=nogit
3636
microbot.repo.url=http://138.201.81.246:8081/repository/microbot-snapshot/
3737
microbot.repo.username=

runelite-client/src/main/java/net/runelite/client/plugins/microbot/breakhandler/breakhandlerv2/BreakHandlerV2Config.java

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import net.runelite.client.plugins.microbot.util.antiban.enums.PlaySchedule;
55
import net.runelite.client.plugins.microbot.util.world.RegionPreference;
66
import net.runelite.client.plugins.microbot.util.world.WorldSelectionMode;
7-
import net.runelite.client.plugins.microbot.breakhandler.breakhandlerv2.MicrobotPluginChoice;
7+
import net.runelite.client.plugins.microbot.breakhandler.breakhandlerv2.PluginStopOption;
88

99
@ConfigGroup(BreakHandlerV2Config.configGroup)
1010
public interface BreakHandlerV2Config extends Config {
@@ -99,12 +99,36 @@ default boolean safetyCheck() {
9999
@ConfigItem(
100100
keyName = "pluginToStop",
101101
name = "Stop Plugin On Break",
102-
description = "Select a Microbot plugin to stop automatically when a break begins.",
102+
description = "Select a Microbot or Plugin Hub plugin to stop automatically when a break begins.",
103103
position = 2,
104104
section = breakBehaviorOptions
105105
)
106-
default MicrobotPluginChoice pluginToStop() {
107-
return MicrobotPluginChoice.NONE;
106+
default String pluginToStop() {
107+
return PluginStopOption.NONE_VALUE;
108+
}
109+
110+
@ConfigItem(
111+
keyName = "stopPluginLeadSeconds",
112+
name = "Stop Lead Time (sec)",
113+
description = "Stop the selected plugin this many seconds before a break starts.",
114+
position = 3,
115+
section = breakBehaviorOptions
116+
)
117+
@Range(min = 0, max = 300)
118+
default int stopPluginLeadSeconds() {
119+
return 0;
120+
}
121+
122+
@ConfigItem(
123+
keyName = "startPluginDelaySeconds",
124+
name = "Restart Delay (sec)",
125+
description = "Wait this many seconds after a break ends before restarting the stopped plugin.",
126+
position = 4,
127+
section = breakBehaviorOptions
128+
)
129+
@Range(min = 0, max = 300)
130+
default int startPluginDelaySeconds() {
131+
return 0;
108132
}
109133

110134
// ========== LOGIN & WORLD SECTION ==========

runelite-client/src/main/java/net/runelite/client/plugins/microbot/breakhandler/breakhandlerv2/BreakHandlerV2Overlay.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import lombok.extern.slf4j.Slf4j;
44
import net.runelite.client.config.ConfigProfile;
55
import net.runelite.client.plugins.microbot.Microbot;
6-
import net.runelite.client.plugins.microbot.breakhandler.breakhandlerv2.MicrobotPluginChoice;
6+
import net.runelite.client.plugins.microbot.breakhandler.breakhandlerv2.PluginStopHelper;
77
import net.runelite.client.plugins.microbot.util.security.LoginManager;
88
import net.runelite.client.ui.overlay.OverlayPanel;
99
import net.runelite.client.ui.overlay.OverlayPosition;
@@ -125,11 +125,14 @@ public Dimension render(Graphics2D graphics) {
125125
.rightColor(config.ignoreWorldSwitching() ? Color.GREEN : Color.LIGHT_GRAY)
126126
.build());
127127

128-
MicrobotPluginChoice stopChoice = config.pluginToStop();
128+
String stopConfigValue = config.pluginToStop();
129+
String stopDisplay = PluginStopHelper.resolveDisplayName(stopConfigValue, Microbot.getPluginManager());
130+
boolean hasStopTarget = !PluginStopHelper.isNone(PluginStopHelper.normalizeStoredValue(stopConfigValue, Microbot.getPluginManager()));
131+
129132
panelComponent.getChildren().add(LineComponent.builder()
130133
.left("Stop plugin:")
131-
.right(stopChoice != null ? stopChoice.toString() : MicrobotPluginChoice.NONE.toString())
132-
.rightColor(stopChoice != null && stopChoice != MicrobotPluginChoice.NONE ? Color.ORANGE : Color.LIGHT_GRAY)
134+
.right(stopDisplay)
135+
.rightColor(hasStopTarget ? Color.ORANGE : Color.LIGHT_GRAY)
133136
.build());
134137

135138
panelComponent.getChildren().add(LineComponent.builder()

runelite-client/src/main/java/net/runelite/client/plugins/microbot/breakhandler/breakhandlerv2/BreakHandlerV2Script.java

Lines changed: 130 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,11 @@
99
import net.runelite.client.plugins.microbot.util.discord.Rs2Discord;
1010
import net.runelite.client.plugins.microbot.util.math.Rs2Random;
1111
import net.runelite.client.plugins.microbot.util.player.Rs2Player;
12-
import net.runelite.client.plugins.microbot.util.security.Login;
1312
import net.runelite.client.plugins.microbot.util.security.LoginManager;
1413
import net.runelite.client.plugins.microbot.util.world.Rs2WorldUtil;
1514
import net.runelite.client.ui.ClientUI;
1615
import net.runelite.http.api.worlds.WorldRegion;
1716
import net.runelite.client.plugins.Plugin;
18-
import net.runelite.client.plugins.microbot.breakhandler.breakhandlerv2.MicrobotPluginChoice;
1917

2018
import javax.inject.Singleton;
2119
import java.awt.Color;
@@ -59,7 +57,9 @@ public BreakHandlerV2Script() {
5957
private String originalWindowTitle = "";
6058
private boolean pluginStopTriggered = false;
6159
private boolean pluginRestartPending = false;
62-
private MicrobotPluginChoice stoppedPluginChoice = MicrobotPluginChoice.NONE;
60+
private String stoppedPluginClassName = PluginStopOption.NONE_VALUE;
61+
private Instant pluginStopEarliestTime = Instant.MIN;
62+
private Instant pluginRestartAllowedAt = Instant.MIN;
6363

6464
// Persisted break keys
6565
private static final String PERSISTED_BREAK_END_KEY = "persistedBreakEnd";
@@ -179,6 +179,8 @@ private void handleWaitingForBreak() {
179179
return;
180180
}
181181

182+
applyPreBreakPluginStopLead();
183+
182184
// When play schedule is enabled, take a break as soon as the schedule window ends
183185
if (config.usePlaySchedule()) {
184186
if (nextBreakTime != null && Instant.now().isAfter(nextBreakTime)) {
@@ -463,6 +465,9 @@ private void handleLoginExtendedSleep() {
463465
private void handleBreakEnding() {
464466
log.info("[BreakHandlerV2] Break cycle complete");
465467

468+
// set restart delay window
469+
pluginRestartAllowedAt = Instant.now().plusSeconds(Math.max(0, config.startPluginDelaySeconds()));
470+
466471
startConfiguredPluginIfNeeded();
467472

468473
// Reset variables
@@ -473,8 +478,8 @@ private void handleBreakEnding() {
473478
preBreakWorld = -1;
474479
unexpectedLogoutDetected = false;
475480
pluginStopTriggered = false;
476-
pluginRestartPending = false;
477-
stoppedPluginChoice = MicrobotPluginChoice.NONE;
481+
pluginRestartAllowedAt = Instant.MIN;
482+
pluginStopEarliestTime = Instant.MIN;
478483

479484
// Unpause scripts
480485
Microbot.pauseAllScripts.set(false);
@@ -696,15 +701,16 @@ private void scheduleNextBreak() {
696701
if (!config.playSchedule().isOutsideSchedule()) {
697702
Duration timeUntilEnd = config.playSchedule().timeUntilScheduleEnds();
698703
nextBreakTime = Instant.now().plus(timeUntilEnd);
699-
log.info("[BreakHandlerV2] Play schedule active ({}), break when schedule ends in {} minutes",
700-
config.playSchedule().name(), timeUntilEnd.toMinutes());
701-
} else {
702-
nextBreakTime = null;
703-
log.info("[BreakHandlerV2] Outside play schedule ({}), currently on break",
704-
config.playSchedule().name());
705-
}
706-
return;
707-
}
704+
log.info("[BreakHandlerV2] Play schedule active ({}), break when schedule ends in {} minutes",
705+
config.playSchedule().name(), timeUntilEnd.toMinutes());
706+
} else {
707+
nextBreakTime = null;
708+
log.info("[BreakHandlerV2] Outside play schedule ({}), currently on break",
709+
config.playSchedule().name());
710+
}
711+
updatePluginStopLeadTime();
712+
return;
713+
}
708714

709715
int minMinutes = config.minPlaytime();
710716
int maxMinutes = config.maxPlaytime();
@@ -713,16 +719,54 @@ private void scheduleNextBreak() {
713719
nextBreakTime = Instant.now().plus(playtimeMinutes, ChronoUnit.MINUTES);
714720

715721
log.info("[BreakHandlerV2] Next break in {} minutes", playtimeMinutes);
722+
723+
updatePluginStopLeadTime();
716724
}
717725

726+
/**
727+
* Calculates when we should pre-stop the selected plugin before the break.
728+
*/
729+
private void updatePluginStopLeadTime() {
730+
if (config == null) {
731+
pluginStopEarliestTime = Instant.MIN;
732+
return;
733+
}
734+
735+
int leadSeconds = Math.max(0, config.stopPluginLeadSeconds());
736+
if (nextBreakTime != null && leadSeconds > 0) {
737+
pluginStopEarliestTime = nextBreakTime.minusSeconds(leadSeconds);
738+
} else {
739+
pluginStopEarliestTime = Instant.MIN;
740+
}
741+
}
742+
743+
/**
744+
* If within the lead window, stop the configured plugin ahead of the break.
745+
*/
746+
private void applyPreBreakPluginStopLead() {
747+
updatePluginStopLeadTime();
748+
749+
if (pluginStopTriggered || config == null) {
750+
return;
751+
}
752+
753+
if (pluginStopEarliestTime == null || pluginStopEarliestTime == Instant.MIN) {
754+
return;
755+
}
756+
757+
if (Instant.now().isAfter(pluginStopEarliestTime) || Instant.now().equals(pluginStopEarliestTime)) {
758+
stopConfiguredPluginIfNeeded();
759+
}
760+
}
761+
718762
/**
719763
* Calculate break duration
720764
*/
721-
private long calculateBreakDuration() {
722-
// If outside play schedule, break until next play time
723-
if (isOutsidePlaySchedule()) {
724-
Duration timeUntilPlaySchedule = config.playSchedule().timeUntilNextSchedule();
725-
long durationMs = timeUntilPlaySchedule.toMillis();
765+
private long calculateBreakDuration() {
766+
// If outside play schedule, break until next play time
767+
if (isOutsidePlaySchedule()) {
768+
Duration timeUntilPlaySchedule = config.playSchedule().timeUntilNextSchedule();
769+
long durationMs = timeUntilPlaySchedule.toMillis();
726770
log.info("[BreakHandlerV2] Play schedule break duration: {} minutes (until next scheduled play time)",
727771
durationMs / 60000);
728772
return durationMs;
@@ -745,47 +789,92 @@ private void stopConfiguredPluginIfNeeded() {
745789
return;
746790
}
747791

748-
MicrobotPluginChoice choice = config.pluginToStop();
749-
if (choice == null || choice == MicrobotPluginChoice.NONE) {
792+
// Respect pre-break lead time while waiting
793+
if (BreakHandlerV2State.getCurrentState() == BreakHandlerV2State.WAITING_FOR_BREAK
794+
&& pluginStopEarliestTime != null
795+
&& pluginStopEarliestTime != Instant.MIN
796+
&& Instant.now().isBefore(pluginStopEarliestTime)) {
750797
return;
751798
}
752799

753-
Class<? extends Plugin> pluginClass = choice.getPluginClass();
754-
if (pluginClass == null) {
755-
log.warn("[BreakHandlerV2] Selected plugin {} has no mapped class; skipping stop request", choice);
756-
pluginStopTriggered = true;
800+
String normalizedSelection = PluginStopHelper.normalizeStoredValue(config.pluginToStop(), Microbot.getPluginManager());
801+
if (PluginStopHelper.isNone(normalizedSelection)) {
802+
return;
803+
}
804+
805+
Plugin pluginInstance = PluginStopHelper.findPluginInstance(
806+
normalizedSelection,
807+
config.pluginToStop(),
808+
Microbot.getPluginManager());
809+
810+
if (pluginInstance == null) {
811+
log.warn("[BreakHandlerV2] Could not resolve plugin to stop for value '{}' (normalized '{}')",
812+
config.pluginToStop(), normalizedSelection);
757813
return;
758814
}
759815

760-
Plugin pluginInstance = Microbot.getPlugin(pluginClass);
761816
boolean wasEnabled = Microbot.isPluginEnabled(pluginInstance);
817+
boolean stopResult = Microbot.stopPlugin(pluginInstance);
818+
boolean nowEnabled = Microbot.isPluginEnabled(pluginInstance);
762819

763-
boolean stopped = Microbot.stopPlugin(pluginClass);
764-
log.info("[BreakHandlerV2] Stop request for {} -> {}", choice, stopped ? "stopped/closed" : "not active");
820+
log.info("[BreakHandlerV2] Stop request for {} -> {} (wasEnabled={}, nowEnabled={}, stopResult={})",
821+
PluginStopHelper.resolveDisplayName(normalizedSelection, Microbot.getPluginManager()),
822+
(!nowEnabled) ? "stopped/closed" : "still active",
823+
wasEnabled, nowEnabled, stopResult);
765824

766-
pluginStopTriggered = true;
767-
if (wasEnabled) {
825+
sleep(5000);
826+
827+
if (!nowEnabled) {
828+
pluginStopTriggered = true;
768829
pluginRestartPending = true;
769-
stoppedPluginChoice = choice;
830+
stoppedPluginClassName = normalizedSelection;
831+
// Ensure any running scripts pause while stopped
832+
Microbot.pauseAllScripts.set(true);
833+
} else {
834+
// Leave pluginStopTriggered false so we can retry on the next tick
835+
pluginRestartPending = false;
770836
}
771837
}
772838

773839
/**
774840
* Starts previously stopped plugin after break ends.
775841
*/
776842
private void startConfiguredPluginIfNeeded() {
777-
if (!pluginRestartPending || stoppedPluginChoice == null || stoppedPluginChoice == MicrobotPluginChoice.NONE) {
843+
if (!pluginRestartPending || PluginStopHelper.isNone(stoppedPluginClassName)) {
778844
return;
779845
}
780846

781-
Class<? extends Plugin> pluginClass = stoppedPluginChoice.getPluginClass();
782-
if (pluginClass != null) {
783-
boolean started = Microbot.startPlugin(pluginClass);
784-
log.info("[BreakHandlerV2] Restart request for {} -> {}", stoppedPluginChoice, started ? "started" : "not started");
847+
if (pluginRestartAllowedAt != null
848+
&& pluginRestartAllowedAt != Instant.MIN
849+
&& Instant.now().isBefore(pluginRestartAllowedAt)) {
850+
return;
785851
}
786852

787-
pluginRestartPending = false;
788-
stoppedPluginChoice = MicrobotPluginChoice.NONE;
853+
Plugin pluginInstance = PluginStopHelper.findPluginInstance(
854+
stoppedPluginClassName,
855+
stoppedPluginClassName,
856+
Microbot.getPluginManager());
857+
858+
if (pluginInstance == null) {
859+
log.warn("[BreakHandlerV2] Could not resolve plugin to restart for stored class '{}'", stoppedPluginClassName);
860+
}
861+
862+
boolean started = pluginInstance != null
863+
? Microbot.startPlugin(pluginInstance)
864+
: Microbot.startPlugin(stoppedPluginClassName);
865+
boolean nowEnabled = Microbot.isPluginEnabled(pluginInstance != null ? pluginInstance : Microbot.getPlugin(stoppedPluginClassName));
866+
867+
log.info("[BreakHandlerV2] Restart request for {} -> {} (startedCall={}, nowEnabled={})",
868+
PluginStopHelper.resolveDisplayName(stoppedPluginClassName, Microbot.getPluginManager()),
869+
nowEnabled ? "running" : "not running",
870+
started, nowEnabled);
871+
872+
if (nowEnabled) {
873+
Microbot.pauseAllScripts.set(false);
874+
pluginRestartPending = false;
875+
stoppedPluginClassName = PluginStopOption.NONE_VALUE;
876+
pluginRestartAllowedAt = Instant.MIN;
877+
}
789878
}
790879

791880
/**
@@ -885,7 +974,9 @@ public void shutdown() {
885974
logoutBreakActive = false;
886975
pluginStopTriggered = false;
887976
pluginRestartPending = false;
888-
stoppedPluginChoice = MicrobotPluginChoice.NONE;
977+
stoppedPluginClassName = PluginStopOption.NONE_VALUE;
978+
pluginRestartAllowedAt = Instant.MIN;
979+
pluginStopEarliestTime = Instant.MIN;
889980
}
890981

891982
private void updateWindowTitle() {

runelite-client/src/main/java/net/runelite/client/plugins/microbot/breakhandler/breakhandlerv2/MicrobotPluginChoice.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,37 @@ public enum MicrobotPluginChoice {
3333
this.pluginClass = pluginClass;
3434
}
3535

36+
/**
37+
* Returns the fully qualified class name for this choice, or {@code null} for NONE.
38+
*/
39+
public String getClassName() {
40+
return pluginClass != null ? pluginClass.getName() : null;
41+
}
42+
43+
/**
44+
* Attempts to resolve a stored configuration value (enum name, display name, or class name)
45+
* back to a {@link MicrobotPluginChoice}.
46+
*
47+
* @param value persisted config value
48+
* @return optional matching choice
49+
*/
50+
public static java.util.Optional<MicrobotPluginChoice> fromConfigValue(String value) {
51+
if (value == null) {
52+
return java.util.Optional.empty();
53+
}
54+
55+
for (MicrobotPluginChoice choice : values()) {
56+
if (choice.name().equalsIgnoreCase(value)
57+
|| choice.displayName.equalsIgnoreCase(value)
58+
|| (choice.pluginClass != null && choice.pluginClass.getName().equalsIgnoreCase(value))
59+
|| (choice.pluginClass != null && choice.pluginClass.getSimpleName().equalsIgnoreCase(value))) {
60+
return java.util.Optional.of(choice);
61+
}
62+
}
63+
64+
return java.util.Optional.empty();
65+
}
66+
3667
@Override
3768
public String toString() {
3869
return displayName;

0 commit comments

Comments
 (0)