structured everything into packages

This commit is contained in:
dtookey 2023-08-07 07:42:12 -04:00
parent 6b082a383a
commit 1b2b24c317
21 changed files with 784 additions and 730 deletions

View File

@ -1,394 +0,0 @@
import java.awt.MouseInfo
import java.awt.Point
import java.awt.Robot
import java.awt.event.InputEvent
import java.util.concurrent.TimeUnit
import kotlin.random.Random
/**
* Interface for controllers that provide input capabilities.
*
* This defines methods for mouse and keyboard input like:
*
* - Moving the mouse
* - Mouse clicks
* - Key presses
* - Scrolling
*
* Classes that implement this interface can serve as input automation controllers.
*/
interface InputController {
/**
* Moves the mouse to the given [Point].
*
* @param point The destination [Point] to move the mouse to.
*/
fun moveMouse(point: Point)
/**
* Performs a mouse click at the current pointer location.
*
* @param button The mouse button to click.
* e.g. [InputEvent.BUTTON1_MASK]
*/
fun mouseClick(button: Int)
/**
* Presses and releases the given key.
*
* @param keyCode The key code of the key to press.
*/
fun keyPress(keyCode: Int)
/**
* Performs a mousewheel scroll in motion.
*
* This will move the scroll wheel forward by the number of ticks
* over the duration. It will sleep for short intervals between
* ticks using the provided sleep duration and variance.
*
* @param sleepDur The base sleep duration between scroll ticks.
* @param sleepDurVariance The variance in sleep duration.
*/
fun scrollIn(sleepDur: Long, sleepDurVariance: Long)
/**
* Performs a mousewheel scroll out motion.
*
* Same as [scrollIn] but moves the scroll wheel backward.
*/
fun scrollOut(sleepDur: Long, sleepDurVariance: Long)
}
interface TemporalController {
/**
* Sleeps for the specified duration.
*
* This uses [TimeUnit.MILLISECONDS] to sleep for the given duration in milliseconds.
*
* @param dur The sleep duration in milliseconds.
*/
fun sleep(dur: Long) {
TimeUnit.MILLISECONDS.sleep(dur)
}
/**
* Sleeps for the specified duration with some variance.
*
* This will sleep for the given duration plus a random variance between 0 inclusive and [variance] exclusive.
* The variance is divided in half to generate a random positive value that is added to the duration.
*
* If the duration is negative or the variance is less than 1, this method
* will return immediately without sleeping.
*
* @param duration The base sleep duration in ms
* @param variance The amount of variance to add in ms. Gets divided in half
* and rolled as two separate random numbers to create a normal distribution
*/
fun sleepWithVariance(duration: Long, variance: Long) {
if (duration < 0 || variance <= 1) {
return
}
val dSize = (variance) / 2
val r1 = Random.nextLong(dSize)
val r2 = Random.nextLong(dSize)
sleep(duration + r1 + r2)
}
}
/**
* Data class to hold wiggle parameters for mouse movement.
*
* This simple data class holds two integer properties for x and y wiggle amounts.
* These are used when generating simulated mouse movements to add some variance
* and randomness to the coordinates.
*
* For example, if a target destination point is (100, 200), the wiggle params
* might generate an actual movement point like (102, 198) to add some randomness.
*
* Usage:
*
* ```
* val controller = DesktopController()
* val wiggle = WiggleParams(xWiggle = 10, yWiggle = 15)
*
* val target = Point(100, 200)
* val actual = controller.getAlmostPoint(target, wiggle) // (104, 197)
* ```
*
* @param xWiggle The max amount of variance in x direction. Default 25.
* @param yWiggle The max amount of variance in y direction. Default 25.
*/
data class WiggleParams(
val xWiggle: Int = 25,
val yWiggle: Int = 25
)
/**
* Interface for controllers that interact with the desktop.
*
* This defines methods for getting desktop state like the mouse pointer
* location.
*
* Classes that implement this can serve as desktop automation controllers.
*/
interface DesktopController {
/**
* Gets the current pointer/mouse location on the desktop.
*
* @return The current [Point] location of the mouse pointer.
*/
fun getPointerLocation(): Point {
return MouseInfo.getPointerInfo().location
}
/**
* Gets a "wiggly" point near the given point.
*
* This takes in a target [Point] and [WiggleParams] and returns a new
* point that is randomly offset from the target point based on the
* wiggle parameters.
*
* This is useful for adding variance to mouse movements.
*
* @param point The target point to wiggle around
* @param params The wiggle parameters
* @return A new [Point] randomly offset from the target point.
*/
fun getAlmostPoint(point: Point, params: WiggleParams): Point {
val xDel = Random.nextInt(0, params.xWiggle)
val yDel = Random.nextInt(0, params.yWiggle)
val xDir = if (Random.nextDouble() > 0.5) {
1
} else {
-1
}
val yDir = if (Random.nextDouble() > 0.5) {
1
} else {
-1
}
return Point(point.x + (xDel * xDir), point.y + (yDel * yDir))
}
}
/**
* Interface for full-featured desktop automation controllers.
*
* Automaton combines capabilities from other interfaces to create a controller that can:
*
* - Get desktop and mouse state information like pointer location via [DesktopController]
*
* - Perform mouse and keyboard input like clicks, key presses, and scrolling via [InputController]
*
* - Handle timing and delays between actions using [TemporalController]
*
* By composing multiple capabilities, Automaton aims to provide a simple yet powerful interface for implementing
* desktop automation routines.
*
* Typical usage involves:
*
* 1. Obtaining an Automaton instance bound to the current OS/desktop
* 2. Calling methods like [moveMouse] and [mouseClick] to perform actions
* 3. Using [sleep] and [sleepWithVariance] to add delays
*
* This interface allows the underlying OS/desktop implementation details to be abstracted and swapped as needed.
*/
interface Automaton : DesktopController, InputController, TemporalController
/**
* Desktop automation controller using java.awt.Robot.
*
* This provides mouse, keyboard, and timing control capabilities by
* wrapping the java.awt.Robot class.
*
* Key features:
*
* - Get current mouse/pointer location
* - Move mouse and perform clicks
* - Keyboard presses and hotkeys
* - Scroll wheel motions
* - Sleep/delay methods with variance
*
* RobotController aims to provide a simple and easy to use API for
* automating desktop interactions and workflows.
*
* Usage example:
*
* ```
* val robot = RobotController()
*
* // Move mouse to 100, 200
* robot.mouseMove(Point(100, 200))
*
* // Left click at current position
* robot.click(InputEvent.BUTTON1_MASK)
*
* // Press A key
* robot.keyPress(KeyEvent.VK_A)
* ```
*
* @param robot The Robot instance to use. A default is created if not provided.
*/
open class RobotController(private val robot: Robot = Robot()) : Automaton {
/**
* Moves the mouse pointer to the given [Point] coordinates.
*
* This will use the [Robot] API to move the mouse to the x,y position.
* It also does validation to retry the move if ended up at incorrect location.
*
* Usage examples:
*
* ```
* val controller = DesktopController()
* controller.moveMouse(Point(100, 200))
* ```
*
*
* ```
* val target = Point(500, 300)
* controller.moveMouse(target)
* ```
*
* @param point The destination [Point] x,y coordinates to move the mouse to.
*
*
*
*/
override fun moveMouse(point: Point) {
robot.mouseMove(point.x, point.y)
//There's a bug in OpenJDK that results in incorrect cursor position in the [Robot.mouseMove] function if using
//a Windows Resolution scale other than 100%. As a result, we have to check that the cursor made it all the way.
//From some anecdotal observation, it has an overshoot/decay pattern sort of like a binary search. The mouse will
//usually be in the correct place within 2-3 loop itterations
repeat(10) {
val rPoint = getPointerLocation()
//here, we check the points and if we're good, we
if (rPoint.x == point.x && rPoint.y == point.y) {
return
} else {
robot.mouseMove(point.x, point.y)
}
}
}
/**
* Performs a mouse click of the specified button.
*
* This uses the Robot to press and release the given mouse button.
*
* A random sleep is added in between pressing and releasing the button
* to add variance and avoid robotic timing.
*
* Example usage:
*
* ```
* val robot = RobotController()
*
* // Perform left click at current mouse position
* robot.click(InputEvent.BUTTON1_MASK)
* ```
*
* @param button The button to click. Must be a valid constant like [InputEvent.BUTTON1_MASK].
*
* Returns immediately If button is negative. Button must be a positive integer.
*/
override fun mouseClick(button: Int) {
//guardian logic
if (button < 0) {
return
}
robot.mousePress(button)
//we add in some random time variance here to appear less robotic
sleepWithVariance(8, 8)
robot.mouseRelease(button)
}
/**
* Presses and releases the given key.
*
* This uses the Robot to simulate pressing and releasing the key with the given key code.
*
* A random sleep is added after pressing the key before releasing it to add variance
* and avoid robotic timing.
*
* Example usage:
*
* ```
* val robot = RobotController()
*
* // Press the 'A' key
* robot.keyPress(KeyEvent.VK_A)
* ```
*
* @param keyCode The key code of the key to press, such as [KeyEvent.VK_A].
*
* Returns immediately if keyCode < 0. This can be useful for skipping actions by passing -1
*/
override fun keyPress(keyCode: Int) {
//guardian logic
if (keyCode < 0) {
return
}
robot.keyPress(keyCode)
//we add in some random time variance here to appear less robotic
sleepWithVariance(8, 8)
robot.keyRelease(keyCode)
}
/**
* Scrolls the mouse wheel down by one unit.
*
* Uses the [Robot.mouseWheel] method to scroll down and then sleeps
* for a random duration between 10-20ms to pace the scrolling.
*
* Example usage:
*
* ```
* val robot = RobotController()
*
* // Scroll down 5 units
* repeat(5) {
* robot.scrollDown()
* }
* ```
*/
override fun scrollOut(sleepDur: Long, sleepDurVariance: Long) {
robot.mouseWheel(1)
sleepWithVariance(sleepDur, sleepDurVariance)
}
/**
* Scrolls the mouse wheel up by one unit.
*
* Uses the [Robot.mouseWheel] method to scroll up and then sleeps for a
* random duration between 10-20ms to pace the scrolling.
*
* Example usage:
*
* ```
* val robot = RobotController()
*
* // Scroll up 10 units
* repeat(10) {
* robot.scrollUp()
* }
* ```
*/
override fun scrollIn(sleepDur: Long, sleepDurVariance: Long) {
robot.mouseWheel(-1)
sleepWithVariance(sleepDur, sleepDurVariance)
}
}

View File

@ -1,14 +1,6 @@
import java.awt.Point
import game_logic.runescape.RunescapeRoutines
fun main() {
val agent = RSOrchestrator.getInstance()
val p = agent.getBankPoint()
repeat(25){
// val newPoint = agent.automaton.getAlmostPoint(p, WiggleParams(10, 10))
val newPoint = p
println("Moving mouse to $newPoint")
agent.automaton.moveMouse(newPoint)
agent.automaton.sleep(500)
}
RunescapeRoutines.fullRunIncense(0, 500, 500, 500)
}

View File

@ -1,157 +0,0 @@
import java.awt.Point
/**
* Interface for common task parameters used across automation routines.
*
* This defines standard fields needed by most routines like total volume,
* volume per step, and the Agent instance.
*
* @property totalVolume The total number of items to process in the routine.
* @property volumePerStep The volume of items to process per step.
* @property agent The Agent instance that will run the routine.
*/
interface TaskParams {
val totalVolume: Int
val volumePerStep: Int
}
/**
* Interface for bank related parameters used in automation routines.
*
* Routines that involve banking items will need bank specific
* configuration like location and preset hotkeys.
*
* This interface encapsulates those common bank parameters. Classes
* that represent task params should implement this if banking is required.
*
* @property bankPoint The Point location of the bank to use.
* @property bankPresetHotkey The bank preset hotkey to withdraw/deposit items.
*/
interface BankParams {
val bankPoint: Point
val bankPresetHotkey: Int
}
/**
* Interface for crafting related parameters used in automation routines.
*
* Routines that involve a crafting action like fletching, cooking, etc will
* need crafting specific configuration like hotkeys and timing.
*
* This interface encapsulates those common crafting parameters. Classes
* that represent task params should implement this if they involve crafting.
*
* @property craftingDialogHotkey The hotkey used to open the crafting dialog.
* @property craftingWaitDurationMillis Base time in ms to wait after crafting.
* @property craftingWaitDurationVarianceMillis Random variance added to wait.
*/
interface CraftingParams {
val craftingDialogHotkey: Int
val craftingWaitDurationMillis: Long
val craftingWaitDurationVarianceMillis: Long
}
/**
* Interface for travel related parameters used in automation routines.
*
* Routines that involve traveling between a bank and activity area (e.g.
* crafting, cooking) will need travel related configuration.
*
* This interface encapsulates those common travel params. Classes that
* represent travel task params should implement this interface.
*
* @property travelPoint The Point destination to travel to.
* @property travelDurationMillis The expected travel time in milliseconds.
* @property travelDurationVarianceMillis Random variance to apply to the
* travel time. This helps simulate human-like travel.
*/
interface TravelParams {
val travelPoint: Point
val travelDurationMillis: Long
val travelDurationVarianceMillis: Long
}
/**
* Task parameters for routines performed while standing in one spot.
*
* This represents routines like fletching, cooking, etc. that are done
* without traveling between a bank and activity area.
*
* @param totalVolume Total number of items to process.
* @param volumePerStep The volume of items to process per iteration.
* @param agent The Agent instance.
* @param bankPoint Location of the bank.
* @param bankPresetHotkey Bank preset hotkey to use.
* @param craftingDialogHotkey Hotkey to open crafting dialog.
* @param craftingWaitDurationMillis Crafting action duration.
* @param craftingWaitDurationVarianceMillis Random variance for duration.
*/
data class StandingTaskParams(
override val totalVolume: Int,
override val volumePerStep: Int,
override val bankPoint: Point,
override val bankPresetHotkey: Int,
override val craftingDialogHotkey: Int,
override val craftingWaitDurationMillis: Long,
override val craftingWaitDurationVarianceMillis: Long
) : TaskParams, BankParams, CraftingParams
/**
* Task parameters for routines that involve traveling.
*
* This encapsulates all the configuration needed for routines where the
* player travels between a bank and activity area for crafting, cooking, etc.
*
* It brings together the common [TaskParams], bank [BankParams], crafting
* [CraftingParams], and travel [TravelParams] parameters into one data class.
*
* @param totalVolume Total number of items to process.
* @param volumePerStep The volume of items per crafting iteration.
* @param agent The Agent instance.
* @param bankPoint The bank location.
* @param travelPoint The travel destination.
* @param bankPresetHotkey Hotkey for bank preset.
* @param craftingDialogHotkey Hotkey to open crafting dialog.
* @param craftingWaitDurationMillis Base crafting action time.
* @param craftingWaitDurationVarianceMillis Crafting time variance.
* @param travelDurationMillis Expected travel time.
* @param travelDurationVarianceMillis Travel time variance.
*/
data class TravelTaskParams(
override val totalVolume: Int,
override val volumePerStep: Int,
override val bankPoint: Point,
override val travelPoint: Point,
override val bankPresetHotkey: Int,
override val craftingDialogHotkey: Int = -1, //all current travel tasks click the thing that starts the crafting dialogue
override val craftingWaitDurationMillis: Long,
override val craftingWaitDurationVarianceMillis: Long,
override val travelDurationMillis: Long,
override val travelDurationVarianceMillis: Long
) : TaskParams, BankParams, CraftingParams, TravelParams
/**
* CommonVolumesPerStep provides constants for common inventory volumes used during routines.
*/
object CommonVolumesPerStep {
/**
* Full inventory volume constant.
*/
const val FullInventory = 28
/**
* Two-reagent full inventory volume constant.
* For example, when combining two items that fill the inventory.
*/
const val TwoReagentFullInventory = 14
/**
* Volume for coating incense sticks with ashes.
*/
const val CoatingIncenseWithAsh = 26
/**
* Volume for infusing incense sticks with herbs.
*/
const val InfusingIncenseWithHerb = 27
}

View File

@ -0,0 +1,26 @@
package controllers
/**
* Interface for full-featured desktop automation controllers.
*
* Automaton combines capabilities from other interfaces to create a controller that can:
*
* - Get desktop and mouse state information like pointer location via [DesktopController]
*
* - Perform mouse and keyboard input like clicks, key presses, and scrolling via [InputController]
*
* - Handle timing and delays between actions using [TemporalController]
*
* By composing multiple capabilities, Automaton aims to provide a simple yet powerful interface for implementing
* desktop automation routines.
*
* Typical usage involves:
*
* 1. Obtaining an Automaton instance bound to the current OS/desktop
* 2. Calling methods like [moveMouse] and [mouseClick] to perform actions
* 3. Using [sleep] and [sleepWithVariance] to add delays
*
* This interface allows the underlying OS/desktop implementation details to be abstracted and swapped as needed.
*/
interface Automaton : DesktopController, InputController, TemporalController

View File

@ -0,0 +1,54 @@
package controllers
import params.WiggleParams
import java.awt.MouseInfo
import java.awt.Point
import kotlin.random.Random
/**
* Interface for controllers that interact with the desktop.
*
* This defines methods for getting desktop state like the mouse pointer
* location.
*
* Classes that implement this can serve as desktop automation controllers.
*/
interface DesktopController {
/**
* Gets the current pointer/mouse location on the desktop.
*
* @return The current [Point] location of the mouse pointer.
*/
fun getPointerLocation(): Point {
return MouseInfo.getPointerInfo().location
}
/**
* Gets a "wiggly" point near the given point.
*
* This takes in a target [Point] and [WiggleParams] and returns a new
* point that is randomly offset from the target point based on the
* wiggle parameters.
*
* This is useful for adding variance to mouse movements.
*
* @param point The target point to wiggle around
* @param params The wiggle parameters
* @return A new [Point] randomly offset from the target point.
*/
fun getAlmostPoint(point: Point, params: WiggleParams): Point {
val xDel = Random.nextInt(0, params.xWiggle)
val yDel = Random.nextInt(0, params.yWiggle)
val xDir = if (Random.nextDouble() > 0.5) {
1
} else {
-1
}
val yDir = if (Random.nextDouble() > 0.5) {
1
} else {
-1
}
return Point(point.x + (xDel * xDir), point.y + (yDel * yDir))
}
}

View File

@ -0,0 +1,59 @@
package controllers
import java.awt.Point
/**
* Interface for controllers that provide input capabilities.
*
* This defines methods for mouse and keyboard input like:
*
* - Moving the mouse
* - Mouse clicks
* - Key presses
* - Scrolling
*
* Classes that implement this interface can serve as input automation controllers.
*/
interface InputController {
/**
* Moves the mouse to the given [Point].
*
* @param point The destination [Point] to move the mouse to.
*/
fun moveMouse(point: Point)
/**
* Performs a mouse click at the current pointer location.
*
* @param button The mouse button to click.
* e.g. [InputEvent.BUTTON1_MASK]
*/
fun mouseClick(button: Int)
/**
* Presses and releases the given key.
*
* @param keyCode The key code of the key to press.
*/
fun keyPress(keyCode: Int)
/**
* Performs a mousewheel scroll in motion.
*
* This will move the scroll wheel forward by the number of ticks
* over the duration. It will sleep for short intervals between
* ticks using the provided sleep duration and variance.
*
* @param sleepDur The base sleep duration between scroll ticks.
* @param sleepDurVariance The variance in sleep duration.
*/
fun scrollIn(sleepDur: Long, sleepDurVariance: Long)
/**
* Performs a mousewheel scroll out motion.
*
* Same as [scrollIn] but moves the scroll wheel backward.
*/
fun scrollOut(sleepDur: Long, sleepDurVariance: Long)
}

View File

@ -1,3 +1,6 @@
package controllers
import util.HelperFunctions
import java.awt.Point
import java.awt.event.InputEvent
@ -223,7 +226,7 @@ interface Orchestrator {
*
* @param totalVolume The total number of units that need to be processed.
* @param volumePerStep The number of units to process per iteration.
* @param task The function to call each iteration, passing the Orchestrator as argument.
* @param task The function to call each iteration, passing the controllers.Orchestrator as argument.
*/
fun doLoop(
totalVolume: Int,

View File

@ -0,0 +1,200 @@
package controllers
import java.awt.Point
import java.awt.Robot
import java.awt.event.InputEvent
/**
* Desktop automation controller using java.awt.Robot.
*
* This provides mouse, keyboard, and timing control capabilities by
* wrapping the java.awt.Robot class.
*
* Key features:
*
* - Get current mouse/pointer location
* - Move mouse and perform clicks
* - Keyboard presses and hotkeys
* - Scroll wheel motions
* - Sleep/delay methods with variance
*
* RobotController aims to provide a simple and easy to use API for
* automating desktop interactions and workflows.
*
* Usage example:
*
* ```
* val robot = RobotController()
*
* // Move mouse to 100, 200
* robot.mouseMove(Point(100, 200))
*
* // Left click at current position
* robot.click(InputEvent.BUTTON1_MASK)
*
* // Press A key
* robot.keyPress(KeyEvent.VK_A)
* ```
*
* @param robot The Robot instance to use. A default is created if not provided.
*/
open class RobotAutomaton(private val robot: Robot = Robot()) : Automaton {
/**
* Moves the mouse pointer to the given [Point] coordinates.
*
* This will use the [Robot] API to move the mouse to the x,y position.
* It also does validation to retry the move if ended up at incorrect location.
*
* Usage examples:
*
* ```
* val controller = DesktopController()
* controller.moveMouse(Point(100, 200))
* ```
*
*
* ```
* val target = Point(500, 300)
* controller.moveMouse(target)
* ```
*
* @param point The destination [Point] x,y coordinates to move the mouse to.
*
*
*
*/
override fun moveMouse(point: Point) {
robot.mouseMove(point.x, point.y)
//There's a bug in OpenJDK that results in incorrect cursor position in the [Robot.mouseMove] function if using
//a Windows Resolution scale other than 100%. As a result, we have to check that the cursor made it all the way.
//From some anecdotal observation, it has an overshoot/decay pattern sort of like a binary search. The mouse will
//usually be in the correct place within 2-3 loop itterations
repeat(10) {
val rPoint = getPointerLocation()
//here, we check the points and if we're good, we
if (rPoint.x == point.x && rPoint.y == point.y) {
return
} else {
robot.mouseMove(point.x, point.y)
}
}
}
/**
* Performs a mouse click of the specified button.
*
* This uses the Robot to press and release the given mouse button.
*
* A random sleep is added in between pressing and releasing the button
* to add variance and avoid robotic timing.
*
* Example usage:
*
* ```
* val robot = RobotController()
*
* // Perform left click at current mouse position
* robot.click(InputEvent.BUTTON1_MASK)
* ```
*
* @param button The button to click. Must be a valid constant like [InputEvent.BUTTON1_MASK].
*
* Returns immediately If button is negative. Button must be a positive integer.
*/
override fun mouseClick(button: Int) {
//guardian logic
if (button < 0) {
return
}
robot.mousePress(button)
//we add in some random time variance here to appear less robotic
sleepWithVariance(8, 8)
robot.mouseRelease(button)
}
/**
* Presses and releases the given key.
*
* This uses the Robot to simulate pressing and releasing the key with the given key code.
*
* A random sleep is added after pressing the key before releasing it to add variance
* and avoid robotic timing.
*
* Example usage:
*
* ```
* val robot = RobotController()
*
* // Press the 'A' key
* robot.keyPress(KeyEvent.VK_A)
* ```
*
* @param keyCode The key code of the key to press, such as [KeyEvent.VK_A].
*
* Returns immediately if keyCode < 0. This can be useful for skipping actions by passing -1
*/
override fun keyPress(keyCode: Int) {
//guardian logic
if (keyCode < 0) {
return
}
robot.keyPress(keyCode)
//we add in some random time variance here to appear less robotic
sleepWithVariance(8, 8)
robot.keyRelease(keyCode)
}
/**
* Scrolls the mouse wheel down by one unit.
*
* Uses the [Robot.mouseWheel] method to scroll down and then sleeps
* for a random duration between 10-20ms to pace the scrolling.
*
* Example usage:
*
* ```
* val robot = RobotController()
*
* // Scroll down 5 units
* repeat(5) {
* robot.scrollDown()
* }
* ```
*/
override fun scrollOut(sleepDur: Long, sleepDurVariance: Long) {
robot.mouseWheel(1)
sleepWithVariance(sleepDur, sleepDurVariance)
}
/**
* Scrolls the mouse wheel up by one unit.
*
* Uses the [Robot.mouseWheel] method to scroll up and then sleeps for a
* random duration between 10-20ms to pace the scrolling.
*
* Example usage:
*
* ```
* val robot = RobotController()
*
* // Scroll up 10 units
* repeat(10) {
* robot.scrollUp()
* }
* ```
*/
override fun scrollIn(sleepDur: Long, sleepDurVariance: Long) {
robot.mouseWheel(-1)
sleepWithVariance(sleepDur, sleepDurVariance)
}
}

View File

@ -0,0 +1,40 @@
package controllers
import java.util.concurrent.TimeUnit
import kotlin.random.Random
interface TemporalController {
/**
* Sleeps for the specified duration.
*
* This uses [TimeUnit.MILLISECONDS] to sleep for the given duration in milliseconds.
*
* @param dur The sleep duration in milliseconds.
*/
fun sleep(dur: Long) {
TimeUnit.MILLISECONDS.sleep(dur)
}
/**
* Sleeps for the specified duration with some variance.
*
* This will sleep for the given duration plus a random variance between 0 inclusive and [variance] exclusive.
* The variance is divided in half to generate a random positive value that is added to the duration.
*
* If the duration is negative or the variance is less than 1, this method
* will return immediately without sleeping.
*
* @param duration The base sleep duration in ms
* @param variance The amount of variance to add in ms. Gets divided in half
* and rolled as two separate random numbers to create a normal distribution
*/
fun sleepWithVariance(duration: Long, variance: Long) {
if (duration < 0 || variance <= 1) {
return
}
val dSize = (variance) / 2
val r1 = Random.nextLong(dSize)
val r2 = Random.nextLong(dSize)
sleep(duration + r1 + r2)
}
}

View File

@ -1,166 +1,15 @@
package game_logic.runescape
import controllers.Automaton
import controllers.RobotAutomaton
import params.WiggleParams
import params.StandingTaskParams
import params.TravelTaskParams
import java.awt.Point
import java.awt.event.KeyEvent
/**
* Interface for a RuneScape orchestrator that can execute bot actions.
*
* This defines the capabilities that a RuneScape orchestrator needs to
* coordinate and perform bot actions like banking, traveling, and crafting.
*
* Implementations will contain the game-specific logic to interact with the
* RuneScape client and APIs to carry out the actions.
*
* Usage example:
* ```
* val orch = RSOrchestrator.getInstance()
* val params = StandingTaskParams(...)
* RSOrchestrator.doStandingTask(orch, params)
* ```
*/
interface RSOrchestrator : Orchestrator {
companion object {
/**
* Performs a standing crafting task loop at the bank.
*
* This handles a simple crafting loop where the player stands at the bank and crafts.
*
* It withdraws items from the bank, crafts a batch of items, deposits crafted items,
* and repeats.
*
* Usage example:
* ```
* val orch = RSOrchestrator.getInstance()
* val params = StandingTaskParams(...)
* RSOrchestrator.doStandingTask(orch, params)
* ```
*
* @param orchestrator The [RSOrchestrator] that will execute the actions.
* @param params The [StandingTaskParams] configuring the task details.
* @return Unit.
*/
fun doStandingTask(orchestrator: RSOrchestrator, params: StandingTaskParams) {
orchestrator.doLoop(params.totalVolume, params.volumePerStep) {
orchestrator.processAtBank(params)
}
}
/**
* Performs a crafting workflow loop that involves traveling between a bank and crafting station.
*
* This handles the overall workflow orchestration of:
* 1. Withdrawing items from the bank.
* 2. Traveling to the crafting station.
* 3. Crafting items.
* 4. Traveling back to the bank when inventory is empty.
*
* It will repeat this loop for the specified total volume of items to craft, doing the given volume per loop iteration.
*
* Usage example:
*
* ```
* val orch = RSOrchestrator.getInstance()
* val params = TravelTaskParams(...)
* RSOrchestrator.doTravelTask(orch, params)
* ```
*
* @param orchestrator The [RSOrchestrator] instance that will execute the actual actions.
* @param params The [TravelTaskParams] configuring the crafting loop details.
* @return Unit.
*/
fun doTravelTask(orchestrator: RSOrchestrator, params: TravelTaskParams) {
orchestrator.doLoop(params.totalVolume, params.volumePerStep) {
orchestrator.processAtStationNearBank(params)
}
}
/**
* Gets an instance of the [RSOrchestrator].
*
* This provides access to the orchestrator instance that can be used to
* coordinate bot actions.
*
* @return The [RSOrchestrator] instance.
*/
fun getInstance(): RSOrchestrator {
return RSAgent()
}
} //end of companion object
/**
* Handles the crafting process when standing at the bank.
*
* This method orchestrates the workflow when standing at the bank:
*
* - Opens the bank interface by left-clicking near the provided bank point location.
* - Withdraws the desired inventory preset using the provided hotkey.
* - Opens the crafting interface using the provided crafting dialogue hotkey.
* - Clicks the default "Make" hotkey to start crafting.
* - Waits for the specified crafting duration plus random variance.
*
* Usage example:
* ```
* val params = StandingTaskParams(...)
* orchestrator.processAtBank(params)
* ```
*
* @param taskParams The StandingTaskParams configuring the task details like bank location, hotkeys, durations etc.
*/
fun processAtBank(taskParams: StandingTaskParams)
/**
* Handles the crafting workflow when at a station near the bank.
*
* This orchestrates the steps to craft items at a station near the bank:
*
* - Travels from the bank to the station by left-clicking near the bank point.
* - Waits for the randomized travel duration.
*
* - Withdraws the preset inventory from the bank using the hotkey.
* - Waits for the randomized bank interaction duration.
*
* - Travels to the crafting station by left-clicking the station point.
* - Waits for the randomized travel duration.
*
* - Opens the crafting interface using the provided hotkey.
* - Clicks the "Make" button using the hotkey to craft items.
* - Waits for the randomized crafting duration.
*
* Usage example:
* ```
* val params = TravelTaskParams(...)
* orchestrator.processAtStationNearBank(params)
* ```
*
* @param taskParams The TravelTaskParams configuring the task details like locations, durations, hotkeys etc.
*/
fun processAtStationNearBank(taskParams: TravelTaskParams)
/**
* Gets the screen point location of the bank.
*
* This returns the x,y screen coordinates where the bank is located, which can be used to interact with the bank.
*
* Usage:
*
* ```
* val bankPoint = orchestrator.getBankLocation()
*
* // Left click the bank location to open the interface
* orchestrator.moveMouseLeftClickAndSleep(bankPoint, 100)
*
* // Withdraw preset inventory at bank
* orchestrator.automaton.keyPress(KeyEvent.VK_F1)
* ```
*
* @return The Point representing the x,y screen coordinates of the bank location.
*/
fun getBankPoint(): Point
}
/**
* Implementation of [RSOrchestrator] using a [RobotController].
* Implementation of [RSOrchestrator] using a [RobotAutomaton].
*
* This class handles executing RuneScape automation tasks by controlling
* the game client via image recognition and input emulation.
@ -179,9 +28,9 @@ interface RSOrchestrator : Orchestrator {
* ```
*
*
* @param automaton The [Automaton] instance used to control the game. Defaults to [RobotController].
* @param automaton The [Automaton] instance used to control the game. Defaults to [RobotAutomaton].
*/
private class RSAgent(override val automaton: Automaton = RobotController()) : RSOrchestrator {
class RSAgent(override val automaton: Automaton = RobotAutomaton()) : RSOrchestrator {
companion object {
/**

View File

@ -0,0 +1,164 @@
package game_logic.runescape
import controllers.Orchestrator
import params.StandingTaskParams
import params.TravelTaskParams
import java.awt.Point
/**
* Interface for a RuneScape orchestrator that can execute bot actions.
*
* This defines the capabilities that a RuneScape orchestrator needs to
* coordinate and perform bot actions like banking, traveling, and crafting.
*
* Implementations will contain the game-specific logic to interact with the
* RuneScape client and APIs to carry out the actions.
*
* Usage example:
* ```
* val orch = RSOrchestrator.getInstance()
* val params = StandingTaskParams(...)
* RSOrchestrator.doStandingTask(orch, params)
* ```
*/
interface RSOrchestrator : Orchestrator {
companion object {
/**
* Performs a standing crafting task loop at the bank.
*
* This handles a simple crafting loop where the player stands at the bank and crafts.
*
* It withdraws items from the bank, crafts a batch of items, deposits crafted items,
* and repeats.
*
* Usage example:
* ```
* val orch = RSOrchestrator.getInstance()
* val params = StandingTaskParams(...)
* RSOrchestrator.doStandingTask(orch, params)
* ```
*
* @param orchestrator The [RSOrchestrator] that will execute the actions.
* @param params The [StandingTaskParams] configuring the task details.
* @return Unit.
*/
fun doStandingTask(orchestrator: RSOrchestrator, params: StandingTaskParams) {
orchestrator.doLoop(params.totalVolume, params.volumePerStep) {
orchestrator.processAtBank(params)
}
}
/**
* Performs a crafting workflow loop that involves traveling between a bank and crafting station.
*
* This handles the overall workflow orchestration of:
* 1. Withdrawing items from the bank.
* 2. Traveling to the crafting station.
* 3. Crafting items.
* 4. Traveling back to the bank when inventory is empty.
*
* It will repeat this loop for the specified total volume of items to craft, doing the given volume per loop iteration.
*
* Usage example:
*
* ```
* val orch = RSOrchestrator.getInstance()
* val params = TravelTaskParams(...)
* RSOrchestrator.doTravelTask(orch, params)
* ```
*
* @param orchestrator The [RSOrchestrator] instance that will execute the actual actions.
* @param params The [TravelTaskParams] configuring the crafting loop details.
* @return Unit.
*/
fun doTravelTask(orchestrator: RSOrchestrator, params: TravelTaskParams) {
orchestrator.doLoop(params.totalVolume, params.volumePerStep) {
orchestrator.processAtStationNearBank(params)
}
}
/**
* Gets an instance of the [RSOrchestrator].
*
* This provides access to the orchestrator instance that can be used to
* coordinate bot actions.
*
* @return The [RSOrchestrator] instance.
*/
fun getInstance(): RSOrchestrator {
return RSAgent()
}
} //end of companion object
/**
* Handles the crafting process when standing at the bank.
*
* This method orchestrates the workflow when standing at the bank:
*
* - Opens the bank interface by left-clicking near the provided bank point location.
* - Withdraws the desired inventory preset using the provided hotkey.
* - Opens the crafting interface using the provided crafting dialogue hotkey.
* - Clicks the default "Make" hotkey to start crafting.
* - Waits for the specified crafting duration plus random variance.
*
* Usage example:
* ```
* val params = StandingTaskParams(...)
* orchestrator.processAtBank(params)
* ```
*
* @param taskParams The StandingTaskParams configuring the task details like bank location, hotkeys, durations etc.
*/
fun processAtBank(taskParams: StandingTaskParams)
/**
* Handles the crafting workflow when at a station near the bank.
*
* This orchestrates the steps to craft items at a station near the bank:
*
* - Travels from the bank to the station by left-clicking near the bank point.
* - Waits for the randomized travel duration.
*
* - Withdraws the preset inventory from the bank using the hotkey.
* - Waits for the randomized bank interaction duration.
*
* - Travels to the crafting station by left-clicking the station point.
* - Waits for the randomized travel duration.
*
* - Opens the crafting interface using the provided hotkey.
* - Clicks the "Make" button using the hotkey to craft items.
* - Waits for the randomized crafting duration.
*
* Usage example:
* ```
* val params = TravelTaskParams(...)
* orchestrator.processAtStationNearBank(params)
* ```
*
* @param taskParams The TravelTaskParams configuring the task details like locations, durations, hotkeys etc.
*/
fun processAtStationNearBank(taskParams: TravelTaskParams)
/**
* Gets the screen point location of the bank.
*
* This returns the x,y screen coordinates where the bank is located, which can be used to interact with the bank.
*
* Usage:
*
* ```
* val bankPoint = orchestrator.getBankLocation()
*
* // Left click the bank location to open the interface
* orchestrator.moveMouseLeftClickAndSleep(bankPoint, 100)
*
* // Withdraw preset inventory at bank
* orchestrator.automaton.keyPress(KeyEvent.VK_F1)
* ```
*
* @return The Point representing the x,y screen coordinates of the bank location.
*/
fun getBankPoint(): Point
}

View File

@ -1,3 +1,7 @@
package game_logic.runescape
import params.StandingTaskParams
import params.TravelTaskParams
import util.HelperFunctions
import java.awt.Point
import java.awt.event.KeyEvent
@ -26,7 +30,7 @@ import java.awt.event.KeyEvent
* Scripts can invoke the routines directly rather than reimplementing the
* underlying actions. Parameters allow customizing volumes, locations etc.
*/
object Routines {
object RunescapeRoutines {
/**
* The duration in milliseconds of one game tick.
*
@ -136,7 +140,7 @@ object Routines {
fun cleanHerbs(volume: Int, agent: RSOrchestrator = RSOrchestrator.getInstance(), bankPoint: Point = agent.getBankPoint()) {
val params = StandingTaskParams(
volume,
CommonVolumesPerStep.FullInventory,
util.Constants.FullInventory,
bankPoint,
KeyEvent.VK_F1,
KeyEvent.VK_1,
@ -175,7 +179,7 @@ object Routines {
fun cutIncenseSticks(volume: Int, agent: RSOrchestrator = RSOrchestrator.getInstance(), bankPoint: Point = agent.getBankPoint()) {
val params = StandingTaskParams(
volume,
CommonVolumesPerStep.FullInventory,
util.Constants.FullInventory,
bankPoint,
KeyEvent.VK_F2,
KeyEvent.VK_2,
@ -215,7 +219,7 @@ object Routines {
fun coatIncenseSticks(volume: Int, agent: RSOrchestrator = RSOrchestrator.getInstance(), bankPoint: Point = agent.getBankPoint()) {
val params = StandingTaskParams(
volume,
CommonVolumesPerStep.CoatingIncenseWithAsh,
util.Constants.CoatingIncenseWithAsh,
bankPoint,
KeyEvent.VK_F3,
KeyEvent.VK_3,
@ -253,7 +257,7 @@ object Routines {
fun infuseIncenseSticks(volume: Int, agent: RSOrchestrator = RSOrchestrator.getInstance(), bankPoint: Point = agent.getBankPoint()) {
val params = StandingTaskParams(
volume,
CommonVolumesPerStep.InfusingIncenseWithHerb,
util.Constants.InfusingIncenseWithHerb,
bankPoint,
KeyEvent.VK_F4,
KeyEvent.VK_4,
@ -292,7 +296,7 @@ object Routines {
fun craftPotionAtBank(volume: Int, agent: RSOrchestrator = RSOrchestrator.getInstance(), bankPoint: Point = agent.getBankPoint()) {
val params = StandingTaskParams(
volume,
CommonVolumesPerStep.FullInventory,
util.Constants.FullInventory,
bankPoint,
KeyEvent.VK_F6,
KeyEvent.VK_MINUS,
@ -342,7 +346,7 @@ object Routines {
val well = agent.promptUserForPoint("Put your mouse over the well...")
val params = TravelTaskParams(
volume,
CommonVolumesPerStep.FullInventory,
util.Constants.FullInventory,
bankPoint,
well,
KeyEvent.VK_F6,

View File

@ -0,0 +1,20 @@
package params
import java.awt.Point
/**
* Interface for bank related parameters used in automation routines.
*
* Routines that involve banking items will need bank specific
* configuration like location and preset hotkeys.
*
* This interface encapsulates those common bank parameters. Classes
* that represent task params should implement this if banking is required.
*
* @property bankPoint The Point location of the bank to use.
* @property bankPresetHotkey The bank preset hotkey to withdraw/deposit items.
*/
interface BankParams {
val bankPoint: Point
val bankPresetHotkey: Int
}

View File

@ -0,0 +1,20 @@
package params
/**
* Interface for crafting related parameters used in automation routines.
*
* Routines that involve a crafting action like fletching, cooking, etc will
* need crafting specific configuration like hotkeys and timing.
*
* This interface encapsulates those common crafting parameters. Classes
* that represent task params should implement this if they involve crafting.
*
* @property craftingDialogHotkey The hotkey used to open the crafting dialog.
* @property craftingWaitDurationMillis Base time in ms to wait after crafting.
* @property craftingWaitDurationVarianceMillis Random variance added to wait.
*/
interface CraftingParams {
val craftingDialogHotkey: Int
val craftingWaitDurationMillis: Long
val craftingWaitDurationVarianceMillis: Long
}

View File

@ -0,0 +1,29 @@
package params
import java.awt.Point
/**
* Task parameters for routines performed while standing in one spot.
*
* This represents routines like fletching, cooking, etc. that are done
* without traveling between a bank and activity area.
*
* @param totalVolume Total number of items to process.
* @param volumePerStep The volume of items to process per iteration.
* @param agent The Agent instance.
* @param bankPoint Location of the bank.
* @param bankPresetHotkey Bank preset hotkey to use.
* @param craftingDialogHotkey Hotkey to open crafting dialog.
* @param craftingWaitDurationMillis Crafting action duration.
* @param craftingWaitDurationVarianceMillis Random variance for duration.
*/
data class StandingTaskParams(
override val totalVolume: Int,
override val volumePerStep: Int,
override val bankPoint: Point,
override val bankPresetHotkey: Int,
override val craftingDialogHotkey: Int,
override val craftingWaitDurationMillis: Long,
override val craftingWaitDurationVarianceMillis: Long
) : TaskParams, BankParams, CraftingParams

View File

@ -0,0 +1,20 @@
package params
import java.awt.Point
/**
* Interface for common task parameters used across automation routines.
*
* This defines standard fields needed by most routines like total volume,
* volume per step, and the Agent instance.
*
* @property totalVolume The total number of items to process in the routine.
* @property volumePerStep The volume of items to process per step.
* @property agent The Agent instance that will run the routine.
*/
interface TaskParams {
val totalVolume: Int
val volumePerStep: Int
}

View File

@ -0,0 +1,23 @@
package params
import java.awt.Point
/**
* Interface for travel related parameters used in automation routines.
*
* Routines that involve traveling between a bank and activity area (e.g.
* crafting, cooking) will need travel related configuration.
*
* This interface encapsulates those common travel params. Classes that
* represent travel task params should implement this interface.
*
* @property travelPoint The Point destination to travel to.
* @property travelDurationMillis The expected travel time in milliseconds.
* @property travelDurationVarianceMillis Random variance to apply to the
* travel time. This helps simulate human-like travel.
*/
interface TravelParams {
val travelPoint: Point
val travelDurationMillis: Long
val travelDurationVarianceMillis: Long
}

View File

@ -0,0 +1,38 @@
package params
import java.awt.Point
/**
* Task parameters for routines that involve traveling.
*
* This encapsulates all the configuration needed for routines where the
* player travels between a bank and activity area for crafting, cooking, etc.
*
* It brings together the common [TaskParams], bank [BankParams], crafting
* [CraftingParams], and travel [TravelParams] parameters into one data class.
*
* @param totalVolume Total number of items to process.
* @param volumePerStep The volume of items per crafting iteration.
* @param agent The Agent instance.
* @param bankPoint The bank location.
* @param travelPoint The travel destination.
* @param bankPresetHotkey Hotkey for bank preset.
* @param craftingDialogHotkey Hotkey to open crafting dialog.
* @param craftingWaitDurationMillis Base crafting action time.
* @param craftingWaitDurationVarianceMillis Crafting time variance.
* @param travelDurationMillis Expected travel time.
* @param travelDurationVarianceMillis Travel time variance.
*/
data class TravelTaskParams(
override val totalVolume: Int,
override val volumePerStep: Int,
override val bankPoint: Point,
override val travelPoint: Point,
override val bankPresetHotkey: Int,
override val craftingDialogHotkey: Int = -1, //all current travel tasks click the thing that starts the crafting dialogue
override val craftingWaitDurationMillis: Long,
override val craftingWaitDurationVarianceMillis: Long,
override val travelDurationMillis: Long,
override val travelDurationVarianceMillis: Long
) : TaskParams, BankParams, CraftingParams, TravelParams

View File

@ -0,0 +1,36 @@
package params
/**
* Data class to hold wiggle parameters for mouse movement.
*
* This simple data class holds two integer properties for x and y wiggle amounts.
* These are used when generating simulated mouse movements to add some variance
* and randomness to the coordinates.
*
* For example, if a target destination point is (100, 200), the wiggle params
* might generate an actual movement point like (102, 198) to add some randomness.
*
* Usage:
*
* ```
* val controller = DesktopController()
* val wiggle = WiggleParams(xWiggle = 10, yWiggle = 15)
*
* val target = Point(100, 200)
* val actual = controller.getAlmostPoint(target, wiggle) // (104, 197)
* ```
*
* @param xWiggle The max amount of variance in x direction. Default 25.
* @param yWiggle The max amount of variance in y direction. Default 25.
*/
data class WiggleParams(
val xWiggle: Int = 25,
val yWiggle: Int = 25
)

View File

@ -0,0 +1,24 @@
package util
object Constants {
/**
* Full inventory volume constant.
*/
const val FullInventory = 28
/**
* Two-reagent full inventory volume constant.
* For example, when combining two items that fill the inventory.
*/
const val TwoReagentFullInventory = 14
/**
* Volume for coating incense sticks with ashes.
*/
const val CoatingIncenseWithAsh = 26
/**
* Volume for infusing incense sticks with herbs.
*/
const val InfusingIncenseWithHerb = 27
}

View File

@ -1,3 +1,7 @@
package util
import controllers.Orchestrator
/**
* A collection of helper functions for common utility tasks.
*/