Skip to content
  •  
  •  
  •  
118 changes: 28 additions & 90 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,95 +1,33 @@
# Microbot Agent Guide

Guidance for AI agents building Microbot scripts with the new `microbot/api` queryable layer.
# Microbot API Guide (for Claude)

## Scope & Paths
- Primary plugin code: `runelite-client/src/main/java/net/runelite/client/plugins/microbot`.
- Queryable API docs: `.../microbot/api/QUERYABLE_API.md`; quick read: `api/README.md`.
- Keep new scripts inside the microbot plugin; share helpers under `microbot/util`.

## Build & Test
- Fast build: `mvn -pl runelite-client -am package` (jar in `runelite-client/target/`).
- Unit tests: `mvn -pl runelite-client test`.
- CI parity: `./ci/build.sh` (runs `mvn verify --settings ci/settings.xml`).

## Style Rules
- Java 11 target, tabs for indentation, braces match `MicrobotPlugin.java`, prefer <120 chars/line.
- Name types in UpperCamelCase, members in lowerCamelCase; configs prefixed with plugin name (e.g., `ExampleConfig`).

## Script Pattern
Pair a RuneLite `Plugin` with a `Script` that runs on a background thread; never sleep on the client thread.

```java
@PluginDescriptor(name = "Gathering Demo")
public class GatheringPlugin extends Plugin {
@Inject private GatheringScript script;
@Override protected void startUp() { script.run(); }
@Override protected void shutDown() { script.shutdown(); }
}

@Slf4j
public class GatheringScript extends Script {
@Override
public boolean run() {
mainScheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(() -> {
try {
if (!Microbot.isLoggedIn() || !super.run()) return;

Rs2TileObjectModel tree = new Rs2TileObjectQueryable()
.withName("Tree")
.where(obj -> !Rs2Player.isAnimating())
.nearest();

if (tree != null) {
tree.click("Chop down");
sleepUntil(() -> Rs2Player.isAnimating(), 3000);
}
} catch (Exception e) {
log.error("Loop error", e);
}
}, 0, 600, TimeUnit.MILLISECONDS); // ~1 tick
return true;
}
}
```

## Queryable API Cheatsheet
- **NPCs**
```java
Rs2NpcModel banker = new Rs2NpcQueryable()
.withNames("Banker", "Bank clerk")
.where(npc -> !npc.isInteracting())
.nearest(15);
if (banker != null) banker.click("Bank");
```
- **Ground items**
```java
Rs2TileItemModel loot = new Rs2TileItemQueryable()
.where(Rs2TileItemModel::isLootAble)
.where(item -> item.getTotalGeValue() >= 3000)
.nearest(10);
if (loot != null) loot.pickup();
```
- **Tile objects**
```java
Rs2TileObjectModel bankChest = new Rs2TileObjectQueryable()
.withNames("Bank chest", "Bank booth")
.nearest(20);
if (bankChest != null && !Rs2Bank.isOpen()) {
bankChest.click("Bank");
sleepUntil(Rs2Bank::isOpen, 5000);
}
```
- **Players**
```java
Rs2PlayerModel ally = new Rs2PlayerQueryable()
.where(Rs2PlayerModel::isFriend)
.within(20)
.nearest();
```

## Safety & Timing
- Always guard logic with `Microbot.isLoggedIn()` and `super.run()`; bail early when paused.
- Use `sleep`/`sleepUntil` only on script threads; wrap client access with `Microbot.getClientThread().runOnClientThread(...)` when needed.
- Wait for state changes after interactions (`Rs2Bank.isOpen()`, `Rs2Player.isAnimating()`, inventory/bank counts).
- Limit query radius with `.within(...)` to reduce overhead and cache results inside a loop when reused.
- Config UI for microbot plugins is rendered via `plugins/microbot/ui/MicrobotConfigPanel` (not the default RuneLite config panel); put config UI changes there.

## Paths & Builds
- Plugin sources live in `runelite-client/src/main/java/net/runelite/client/plugins/microbot`.
- The queryable API lives in `.../microbot/api`; full guide: `.../microbot/api/QUERYABLE_API.md`.
- Quick builds: `mvn -pl runelite-client -am package`; tests: `mvn -pl runelite-client test`.

## Queryable API Quick Reference
Prefer the queryable API over legacy util calls.

## Interaction & Timing Tips
- Never sleep on the RuneLite client thread; use the script thread with `sleep(...)` / `sleepUntil(...)`.
- After interactions, wait for state changes (e.g., `Rs2Bank.isOpen()`, `Rs2Player.isAnimating()`).
- Limit search radius with `.within(...)` to reduce overhead, and cache query results when reusing in a loop.

## Helpful References
- Example templates: `runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/`.
- API examples: `api/*/` directories contain `*ApiExample.java` files for NPCs, tile items, players, and objects.
- Core utilities (legacy but still useful): `microbot/util` (e.g., `Rs2Inventory`, `Rs2Bank`, `Rs2Walker`).

## QuestScript Loop (Quest Helper)
- `QuestScript.run(config, plugin)` sets a 400–1000ms fixed-delay loop; exits early if quest helper is toggled off, not logged in, paused (`super.run()`), or no quest is selected, and waits out player animations.
- Captures the active `QuestStep`, marks when dialogue starts, auto-chooses matching dialogue options, and clicks highlighted widgets (special shop buy for Pirate's Treasure).
- Runs quest-specific logic via `QuestRegistry.getQuest(...).executeCustomLogic()` (Pirate's Treasure gets the plugin injected).
- While incomplete: handles dialogue quirks (Cook's Assistant/Pirate's Treasure), exits cutscenes, clears walk targets when talking, and manages reachability flags.
- Requirement phase: equips required items, warns on missing items (rate-limited), and attempts to acquire them by looting nearby or walking toward the defined point; prioritizes item-on-item detailed steps before other step types.
- Dispatch order: `ConditionalStep` → `NpcStep` → `ObjectStep` → `DigStep` → `PuzzleStep`; per-type handlers choose the correct menu action, manage line-of-sight and walkable tiles, and call `sleepUntil` to wait for movement/animation/interactions before looping.
83 changes: 8 additions & 75 deletions CLAUDE.MD
Original file line number Diff line number Diff line change
Expand Up @@ -7,84 +7,9 @@ Short notes for writing automation scripts with the Microbot plugin inside RuneL
- The queryable API lives in `.../microbot/api`; full guide: `.../microbot/api/QUERYABLE_API.md`.
- Quick builds: `mvn -pl runelite-client -am package`; tests: `mvn -pl runelite-client test`.

## Script Skeleton
Use a Plugin + Script pair. Keep sleeps off the client thread and always check login state.

```java
@PluginDescriptor(name = "Goblin Demo", description = "Queryable API example")
public class GoblinDemoPlugin extends Plugin {
@Inject private GoblinDemoScript script;

@Override protected void startUp() { script.run(); }
@Override protected void shutDown() { script.shutdown(); }
}

@Slf4j
public class GoblinDemoScript extends Script {
@Override
public boolean run() {
mainScheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(() -> {
try {
if (!Microbot.isLoggedIn() || !super.run()) return;

Rs2NpcModel target = new Rs2NpcQueryable()
.withName("Goblin")
.where(npc -> !npc.isInteracting())
.nearest(10);

if (target != null && !Rs2Player.isInCombat()) {
target.click("Attack");
sleepUntil(() -> Rs2Player.isInCombat(), 2000);
}
} catch (Exception e) {
log.error("Loop error", e);
}
}, 0, 600, TimeUnit.MILLISECONDS); // ~1 game tick
return true;
}
}
```

## Queryable API Quick Reference
Prefer the queryable API over legacy util calls.

- **NPCs**
```java
Rs2NpcModel banker = new Rs2NpcQueryable()
.withNames("Banker", "Bank clerk")
.where(npc -> !npc.isInteracting())
.nearest(15);
if (banker != null) banker.click("Bank");
```

- **Ground items**
```java
Rs2TileItemModel loot = new Rs2TileItemQueryable()
.where(Rs2TileItemModel::isLootAble)
.where(item -> item.getTotalGeValue() >= 5000)
.nearest(10);
if (loot != null) loot.pickup();
```

- **Tile objects**
```java
Rs2TileObjectModel tree = new Rs2TileObjectQueryable()
.where(obj -> obj.getName() != null && obj.getName().contains("Tree"))
.nearest();
if (tree != null && !Rs2Player.isAnimating()) {
tree.click("Chop down");
sleepUntil(() -> Rs2Player.isAnimating(), 3000);
}
```

- **Players**
```java
Rs2PlayerModel teammate = new Rs2PlayerQueryable()
.where(Rs2PlayerModel::isFriend)
.within(20)
.nearest();
```

## Interaction & Timing Tips
- Never sleep on the RuneLite client thread; use the script thread with `sleep(...)` / `sleepUntil(...)`.
- After interactions, wait for state changes (e.g., `Rs2Bank.isOpen()`, `Rs2Player.isAnimating()`).
Expand All @@ -94,3 +19,11 @@ Prefer the queryable API over legacy util calls.
- Example templates: `runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/`.
- API examples: `api/*/` directories contain `*ApiExample.java` files for NPCs, tile items, players, and objects.
- Core utilities (legacy but still useful): `microbot/util` (e.g., `Rs2Inventory`, `Rs2Bank`, `Rs2Walker`).

## QuestScript Loop (Quest Helper)
- `QuestScript.run(config, plugin)` sets a 400–1000ms fixed-delay loop; exits early if quest helper is toggled off, not logged in, paused (`super.run()`), or no quest is selected, and waits out player animations.
- Captures the active `QuestStep`, marks when dialogue starts, auto-chooses matching dialogue options, and clicks highlighted widgets (special shop buy for Pirate's Treasure).
- Runs quest-specific logic via `QuestRegistry.getQuest(...).executeCustomLogic()` (Pirate's Treasure gets the plugin injected).
- While incomplete: handles dialogue quirks (Cook's Assistant/Pirate's Treasure), exits cutscenes, clears walk targets when talking, and manages reachability flags.
- Requirement phase: equips required items, warns on missing items (rate-limited), and attempts to acquire them by looting nearby or walking toward the defined point; prioritizes item-on-item detailed steps before other step types.
- Dispatch order: `ConditionalStep` → `NpcStep` → `ObjectStep` → `DigStep` → `PuzzleStep`; per-type handlers choose the correct menu action, manage line-of-sight and walkable tiles, and call `sleepUntil` to wait for movement/animation/interactions before looping.
55 changes: 55 additions & 0 deletions docs/api/MouseMacroRecorder.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Mouse Macro Recorder

The Mouse Macro Recorder plugin captures mouse movements and menu entry clicks in the client so you can reuse the
sequence later (e.g., for scripting or diagnostics).

## Plugin Configuration
Use the plugin configuration toggles to control recording:
- **Recording enabled**: clears existing events and begins capturing moves/clicks.
- **Clear recording**: removes all recorded events.
- **Export JSON to clipboard**: formats the current recording as JSON and copies it to the clipboard.

## Recorded format
Each event includes a timestamp offset, canvas coordinates, and (for clicks) the clicked `MenuEntry` metadata.

```json
{
"startedAtEpochMs": 1716220000000,
"events": [
{
"type": "MOVE",
"offsetMs": 120,
"x": 512,
"y": 338,
"menuEntry": null
},
{
"type": "CLICK",
"offsetMs": 420,
"x": 516,
"y": 340,
"menuEntry": {
"option": "Chop down",
"target": "Tree",
"type": "GAME_OBJECT_FIRST_OPTION",
"identifier": 1276,
"param0": 48,
"param1": 57,
"itemId": -1,
"itemOp": 0,
"worldViewId": 0,
"forceLeftClick": false,
"deprioritized": false,
"widgetId": null
}
}
]
}
```

## Configuration
Movement sampling controls live under **Recording** in the plugin config:
- **Record mouse movement**: enable or disable movement tracking.
- **Movement sample interval (ms)**: minimum time between recorded move events.
- **Movement min distance**: minimum pixel distance for a movement sample to be kept.
- **Max recorded events**: stops recording after reaching the limit.
2 changes: 1 addition & 1 deletion docs/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ Everything that will help you make scripts is under the **util** folder
### [Rs2MiniMap](api/Rs2MiniMap.md)
### [Rs2Widget](api/Rs2Widget.md)
### [Rs2Tile](api/Rs2Tile.md)
### [MouseMacroRecorder](api/MouseMacroRecorder.md)

---

Expand Down Expand Up @@ -215,4 +216,3 @@ Coming soon!
---

**Are you stuck? Join our [Discord](https://discord.gg/zaGrfqFEWE) server.**

2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ project.build.group=net.runelite
project.build.version=1.12.10

glslang.path=
microbot.version=2.1.5
microbot.version=2.1.6
microbot.commit.sha=nogit
microbot.repo.url=http://138.201.81.246:8081/repository/microbot-snapshot/
microbot.repo.username=
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package net.runelite.client.config;

import java.util.Objects;
import java.util.UUID;
import lombok.Getter;
import lombok.NoArgsConstructor;
import net.runelite.client.config.ConfigSerializer;

/**
* Marker type for rendering a clickable button in the config panel.
*/
@Getter
@NoArgsConstructor
@ConfigSerializer(ConfigButtonSerializer.class)
public class ConfigButton
{
private String id = UUID.randomUUID().toString();

public ConfigButton(String id)
{
this.id = id;
}

@Override
public boolean equals(Object o)
{
if (this == o)
{
return true;
}
if (!(o instanceof ConfigButton))
{
return false;
}
ConfigButton that = (ConfigButton) o;
return Objects.equals(id, that.id);
}

@Override
public int hashCode()
{
return Objects.hash(id);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package net.runelite.client.config;

public class ConfigButtonSerializer implements Serializer<ConfigButton>
{
@Override
public String serialize(ConfigButton configButton)
{
return configButton != null ? configButton.getId() : null;
}

@Override
public ConfigButton deserialize(String string)
{
return string == null ? new ConfigButton() : new ConfigButton(string);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co

This is a multi-module Maven project with Java 11 as the target version. The main automation code lives in `runelite-client/src/main/java/net/runelite/client/plugins/microbot/`.

**Config UI note:** Microbot plugins use the custom `MicrobotConfigPanel` (under `plugins/microbot/ui`) for config rendering. Any Microbot config UI tweaks—buttons, layouts, or controls—belong there rather than RuneLite’s default config panel.

---

## Build and Test Commands
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ public int getId()
return npc.getId();
}

public int getIndex()
{
return npc.getIndex();
}


// Enhanced utility methods for cache operations

Expand Down
Loading