Skip to content

Commit 2680a5d

Browse files
author
debajit gayen
committed
tuned mouse, improved stop(), improved demo
1 parent e7e8dad commit 2680a5d

File tree

6 files changed

+123
-43
lines changed

6 files changed

+123
-43
lines changed

src/main/java/com/chromascape/base/BaseScript.java

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import static java.time.temporal.ChronoUnit.MINUTES;
44

55
import com.chromascape.controller.Controller;
6+
import com.chromascape.utils.core.runtime.HotkeyListener;
7+
import com.chromascape.utils.core.runtime.ScriptStoppedException;
68
import com.chromascape.web.logs.LogService;
79
import java.time.LocalTime;
810

@@ -20,6 +22,7 @@ public abstract class BaseScript {
2022
private final Controller controller;
2123
private final int duration; // Duration to run the script in minutes
2224
private final LogService logger;
25+
private final HotkeyListener hotkeyListener;
2326
private boolean running = true;
2427
private LocalTime startTime;
2528

@@ -34,6 +37,7 @@ public BaseScript(final boolean isFixed, final int duration, final LogService lo
3437
controller = new Controller(isFixed, logger);
3538
this.duration = duration;
3639
this.logger = logger;
40+
this.hotkeyListener = new HotkeyListener(this);
3741
}
3842

3943
/**
@@ -45,35 +49,51 @@ public BaseScript(final boolean isFixed, final int duration, final LogService lo
4549
*
4650
* <p>This method blocks until completion.
4751
*/
48-
public void run() {
52+
public final void run() {
4953
startTime = LocalTime.now();
5054
LocalTime endTime = startTime.plusMinutes(duration);
5155
logger.addLog("Starting. Script will run for " + duration + " minutes.");
5256
controller.init();
53-
while (startTime.isBefore(endTime) && running) {
54-
if (Thread.currentThread().isInterrupted()) {
55-
logger.addLog("Thread interrupted, stopping.");
56-
break;
57+
hotkeyListener.start();
58+
59+
try {
60+
while (running && LocalTime.now().isBefore(endTime)) {
61+
if (Thread.currentThread().isInterrupted()) {
62+
logger.addLog("Thread interrupted, exiting.");
63+
break;
64+
}
65+
try {
66+
cycle();
67+
} catch (ScriptStoppedException e) {
68+
logger.addLog("Cycle interrupted: " + e.getMessage());
69+
break;
70+
} catch (Exception e) {
71+
logger.addLog("Exception in cycle: " + e.getMessage());
72+
break;
73+
}
5774
}
58-
cycle();
59-
startTime = LocalTime.now();
75+
} finally {
76+
logger.addLog("Stopping and cleaning up.");
77+
controller.shutdown();
6078
}
61-
logger.addLog("Finished running script. Exiting.");
62-
controller.shutdown();
79+
logger.addLog("Finished running script.");
6380
}
6481

6582
/**
6683
* Stops the script execution.
6784
*
68-
* <p>Can be called externally (e.g., via UI controls or programmatically) to request a graceful
69-
* stop of the running script. If the script is already stopped, this method does nothing.
85+
* <p>Can be called externally (e.g., via UI controls or programmatically) to request an immediate
86+
* stop of the running script via the {@code ScriptStoppedException}. If the script is already
87+
* stopped, this method does nothing.
7088
*/
7189
public void stop() {
7290
if (!running) {
7391
return;
7492
}
7593
logger.addLog("Stop requested");
7694
running = false;
95+
hotkeyListener.stop();
96+
throw new ScriptStoppedException();
7797
}
7898

7999
/**

src/main/java/com/chromascape/controller/Controller.java

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import com.chromascape.utils.core.input.keyboard.VirtualKeyboardUtils;
44
import com.chromascape.utils.core.input.mouse.VirtualMouseUtils;
55
import com.chromascape.utils.core.input.remoteinput.Kinput;
6-
import com.chromascape.utils.core.runtime.HotkeyListener;
76
import com.chromascape.utils.core.screen.window.ScreenManager;
87
import com.chromascape.utils.core.screen.window.WindowHandler;
98
import com.chromascape.utils.domain.zones.ZoneManager;
@@ -30,7 +29,6 @@ private enum ControllerState {
3029
private final boolean isFixed;
3130

3231
private Kinput kinput;
33-
private HotkeyListener hotkeyListener;
3432
private VirtualMouseUtils virtualMouseUtils;
3533
private VirtualKeyboardUtils virtualKeyboardUtils;
3634
private ZoneManager zoneManager;
@@ -60,10 +58,6 @@ public void init() {
6058
// Obtain process ID of the target window to initialize input injection
6159
kinput = new Kinput(WindowHandler.getPid(WindowHandler.getTargetWindow()));
6260

63-
// Start listening for hotkeys to allow emergency runtime control
64-
hotkeyListener = new HotkeyListener(this);
65-
hotkeyListener.start();
66-
6761
// Ensure the target window is focused for input simulation
6862
ScreenManager.focusWindow(WindowHandler.getTargetWindow());
6963
boolean isFullscreen = ScreenManager.isWindowFullscreen(WindowHandler.getTargetWindow());
@@ -88,7 +82,6 @@ public void init() {
8882
public void shutdown() {
8983
state = ControllerState.STOPPED;
9084
kinput.destroy();
91-
hotkeyListener.stop();
9285
logger.addLog("Shutting down");
9386
}
9487

src/main/java/com/chromascape/scripts/DemoWineScript.java

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public class DemoWineScript extends BaseScript {
3131
private static final String grapes = "/images/user/Grapes.png";
3232
private static final String jugs = "/images/user/Jug_of_water.png";
3333
private static final String dumpBank = "/images/user/Dump_bank.png";
34+
private static final String unfermented = "/images/user/Unfermented.png";
3435

3536
private static final int MAX_ATTEMPTS = 15;
3637
private static final int INVENT_SLOT_GRAPES = 13;
@@ -60,30 +61,41 @@ public DemoWineScript(boolean isFixed, int duration, LogService logger) {
6061
*/
6162
@Override
6263
protected void cycle() {
63-
6464
if (bankFlag) {
6565
clickBank(); // Open the bank once at the start of the script
66-
Sleeper.waitRandomMillis(150, 300);
66+
Sleeper.waitRandomMillis(700, 900);
6767
bankFlag = false;
6868
// Cannot start in bank because UI needs to initialise
6969
}
7070

71-
clickImage(grapes, "medium", 0.085); // Take out grapes
71+
clickImage(grapes, "fast", 0.07); // Take out grapes
7272
Sleeper.waitRandomMillis(300, 600);
73+
7374
clickImage(jugs, "slow", 0.065); // Take out water jugs
7475
Sleeper.waitRandomMillis(400, 500);
76+
7577
pressEscape(); // Exit bank UI
7678
Sleeper.waitRandomMillis(600, 800);
77-
clickInventSlot(INVENT_SLOT_JUGS, "medium"); // Click the jugs of water in the inventory
79+
80+
clickInventSlot(INVENT_SLOT_JUGS, "fast"); // Click the jugs of water in the inventory
7881
Sleeper.waitRandomMillis(400, 800);
82+
7983
clickInventSlot(INVENT_SLOT_GRAPES, "slow"); // Use the jugs on the grapes to start making wine
8084
Sleeper.waitRandomMillis(800, 900);
85+
8186
pressSpace(); // Accept the start button
82-
Sleeper.waitRandomMillis(18000, 20000); // Wait for wines to combine
87+
Sleeper.waitRandomMillis(17000, 18000); // Wait for wines to combine
88+
8389
clickBank(); // Open the bank to drop off items
84-
Sleeper.waitRandomMillis(400, 600);
85-
clickImage(dumpBank, "medium", 0.03); // Put the fermenting wines in the bank to repeat
86-
Sleeper.waitRandomMillis(300, 600);
90+
Sleeper.waitRandomMillis(700, 900);
91+
92+
clickImage(dumpBank, "medium", 0.055); // Put the fermenting wines in the bank to repeat
93+
Sleeper.waitRandomMillis(400, 500);
94+
95+
if (checkIfImageExists(unfermented, 0.055)) { // Repeating because bank is weird
96+
controller().mouse().leftClick();
97+
Sleeper.waitRandomMillis(600, 800);
98+
}
8799
}
88100

89101
/**
@@ -161,12 +173,14 @@ private void clickBank() {
161173
* Searches for the provided image template within the current game view, then clicks a random
162174
* point within the detected bounding box if the match exceeds the defined threshold.
163175
*
164-
* @param image the BufferedImage template to locate and click within the game view
176+
* @param imagePath the BufferedImage template to locate and click within the game view
177+
* @param speed the speed that the mouse moves to click the image
178+
* @param threshold the openCV threshold to decide if a match exists
165179
*/
166-
private void clickImage(String image, String speed, double threshold) {
180+
private void clickImage(String imagePath, String speed, double threshold) {
167181
try {
168182
BufferedImage gameView = ScreenManager.captureWindow();
169-
Rectangle boundingBox = TemplateMatching.match(image, gameView, threshold, false);
183+
Rectangle boundingBox = TemplateMatching.match(imagePath, gameView, threshold, false);
170184

171185
if (boundingBox == null || boundingBox.isEmpty()) {
172186
logger.addLog("Template match failed: No valid bounding box.");
@@ -189,7 +203,8 @@ private void clickImage(String image, String speed, double threshold) {
189203
/**
190204
* Clicks a random point within the bounding box of a given inventory slot.
191205
*
192-
* @param slot the index of the inventory slot to click
206+
* @param slot the index of the inventory slot to click (0-27)
207+
* @param speed the speed that the mouse moves to click the image
193208
*/
194209
private void clickInventSlot(int slot, String speed) {
195210
try {
@@ -211,4 +226,30 @@ private void clickInventSlot(int slot, String speed) {
211226
stop();
212227
}
213228
}
229+
230+
/**
231+
* Checks if an image exists on the screen and returns a boolean referring to if it was detected.
232+
*
233+
* @param imagePath the path to the image being searched on screen
234+
* @param threshold the openCV threshold to decide if a match exists
235+
* @return true if the image exists on screen, else false
236+
*/
237+
private boolean checkIfImageExists(String imagePath, double threshold) {
238+
try {
239+
BufferedImage gameView = ScreenManager.captureWindow();
240+
Rectangle boundingBox = TemplateMatching.match(imagePath, gameView, threshold, false);
241+
242+
if (boundingBox == null || boundingBox.isEmpty()) {
243+
logger.addLog("Template match failed: No valid bounding box.");
244+
return false;
245+
}
246+
247+
return true;
248+
249+
} catch (Exception e) {
250+
logger.addLog("clickImage failed: " + e.getMessage());
251+
stop();
252+
}
253+
return false;
254+
}
214255
}

src/main/java/com/chromascape/utils/core/input/mouse/MousePathing.java

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,10 @@ private static int[] calculateOffset(final int distance) {
184184
return new int[] {10, 30};
185185
} else if (distance >= 100) {
186186
return new int[] {5, 10};
187-
} else {
187+
} else if (distance >= 20) {
188188
return new int[] {3, 5};
189+
} else {
190+
return new int[] {2, 4};
189191
}
190192
}
191193

@@ -241,17 +243,19 @@ private static double calculateEasing(final int distance) {
241243
if (distance >= 1200) {
242244
return 16;
243245
} else if (distance >= 1000) {
244-
return 12;
246+
return 14;
245247
} else if (distance >= 800) {
246-
return 10;
248+
return 12;
247249
} else if (distance >= 600) {
248-
return 8;
250+
return 10;
249251
} else if (distance >= 400) {
250-
return 6;
252+
return 8;
251253
} else if (distance >= 200) {
252-
return 5;
253-
} else {
254+
return 6;
255+
} else if (distance >= 100) {
254256
return 4;
257+
} else {
258+
return 2;
255259
}
256260
}
257261
}

src/main/java/com/chromascape/utils/core/runtime/HotkeyListener.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.chromascape.utils.core.runtime;
22

3+
import com.chromascape.base.BaseScript;
34
import com.chromascape.controller.Controller;
45
import com.github.kwhat.jnativehook.GlobalScreen;
56
import com.github.kwhat.jnativehook.NativeHookException;
@@ -17,7 +18,7 @@
1718
*/
1819
public class HotkeyListener implements NativeKeyListener {
1920

20-
private final Controller controller;
21+
private final BaseScript baseScript;
2122

2223
// These track whether the shutdown hotkeys are currently being held
2324
private boolean equals = false;
@@ -26,10 +27,10 @@ public class HotkeyListener implements NativeKeyListener {
2627
/**
2728
* Constructs a new HotkeyListener bound to a specific Controller instance.
2829
*
29-
* @param controller The controller whose shutdown method will be triggered by the hotkey.
30+
* @param baseScript The controller whose shutdown method will be triggered by the hotkey.
3031
*/
31-
public HotkeyListener(final Controller controller) {
32-
this.controller = controller;
32+
public HotkeyListener(final BaseScript baseScript) {
33+
this.baseScript = baseScript;
3334
}
3435

3536
/**
@@ -74,12 +75,12 @@ public void nativeKeyPressed(final NativeKeyEvent key) {
7475
if (key.getKeyCode() == NativeKeyEvent.VC_EQUALS) {
7576
equals = true;
7677
if (minus) {
77-
controller.shutdown();
78+
baseScript.stop();
7879
}
7980
} else if (key.getKeyCode() == NativeKeyEvent.VC_MINUS) {
8081
minus = true;
8182
if (equals) {
82-
controller.shutdown();
83+
baseScript.stop();
8384
}
8485
}
8586
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.chromascape.utils.core.runtime;
2+
3+
/**
4+
* Exception used to indicate that a running script has been requested to stop.
5+
*
6+
* <p>This unchecked exception is thrown internally to signal that the script execution
7+
* should be terminated gracefully. It can be caught by the script runner to halt execution
8+
* without treating the stop as an error.
9+
*
10+
* <p>Typically, this exception is thrown by calling {@code stop()} methods in the script
11+
* lifecycle to immediately exit the current execution cycle.
12+
*/
13+
public class ScriptStoppedException extends RuntimeException {
14+
15+
/**
16+
* Constructs a new ScriptStoppedException with a default message.
17+
*/
18+
public ScriptStoppedException() {
19+
super("Script stopped");
20+
}
21+
}

0 commit comments

Comments
 (0)