Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 43 additions & 97 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,98 +1,44 @@
# Microbot API Guide (for Claude)
# Microbot Project Notes (for Codex)

## Microbot at a Glance
Microbot is a RuneLite-based Old School RuneScape client fork with an always-on hidden plugin that hosts automation scripts. It packages a shaded client for end users and keeps developer ergonomics via Queryable caches and script helpers under `microbot/util`. The build mirrors upstream RuneLite as a composite Gradle setup.

## Tech Stack
- Java target 11 (develop/run with JDK 17+), Gradle wrapper, composite builds (`cache`, `runelite-api`, `runelite-client`, `runelite-gradle-plugin`, `runelite-jshell`).
- RuneLite client + plugin APIs, LWJGL, Guice, Lombok, OkHttp, Gson, Logback/SLF4J.
- CI helper `ci/build.sh` bootstraps `glslang` then runs `./gradlew :buildAll`.

## Repo Map
- `runelite-client/` – Main client (`:client`) containing the Microbot plugin and shaded jar assembly.
- `runelite-api/` – RuneLite API included build consumed by the client.
- `runelite-gradle-plugin/` – Gradle plugins for assemble/index/jarsign tasks.
- `runelite-jshell/` – JShell support artifacts.
- `cache/` – Cache tools/build used by RuneLite.
- `docs/` – User/dev docs and static site assets.
- `config/` – Shared Checkstyle configuration.
- `ci/` – CI build helper script.

## How to Validate Changes
- Fast sanity: `./gradlew :client:compileJava`
- Full build (all included builds): `./gradlew buildAll`
- Assemble shaded client: `./gradlew :client:assemble` (creates `runelite-client/build/libs/*-shaded.jar` and `microbot-<version>.jar`)
- Tests are disabled by default via Gradle config; enable/selectively run if you add tests (`./gradlew :client:runTests` or `runDebugTests`).

## Non-Negotiable Rules
- Never instantiate caches or queryables directly; always use `Microbot.getRs2XxxCache().query()` or `.getStream()` (see `runelite-client/src/main/java/net/runelite/client/plugins/microbot/api/QUERYABLE_API.md`).
- Do not block or sleep on the RuneLite client thread; long work belongs on script/executor threads.
- Keep logging minimal; avoid PII/session identifiers and respect existing log levels/patterns.
- Preserve the hidden/always-on nature of `MicrobotPlugin` and its config panel wiring.
- Follow Checkstyle/Lombok patterns already in the codebase; do not downgrade security (e.g., telemetry token handling, HTTP clients) without discussion.

## Review Guidelines
- P0: Anything that can crash the client, block the client thread, break world hopping/login, corrupt cache/queryable invariants, or expose credentials/telemetry tokens.
- P1: Regressions to script loop timing, overlay correctness, plugin discovery/config panels, packaging (shaded jar/version props), or build reproducibility.
- Check threading (client vs script), cache access patterns, Gradle task wiring, and error handling around network calls.

## When Unsure
- Start with `docs/ARCHITECTURE.md` and `docs/decisions/` for background.
- API and script usage: `runelite-client/src/main/java/net/runelite/client/plugins/microbot/api/README.md` and `QUERYABLE_API.md`.
- Development/setup: `docs/development.md`; installation: `docs/installation.md`.
- Still unclear? Ask in Discord (link in `README.md`) or leave TODO with assumption noted.

## 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`.
- 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: `./gradlew :runelite-client:compileJava`; full build: `./gradlew build`.

## Cache & Queryable API

### Singleton Pattern
All caches are `@Singleton` classes injected via Guice and exposed through `Microbot` static getters:

```java
@Singleton
public final class Rs2NpcCache {
@Inject
public Rs2NpcCache(Client client, ClientThread clientThread) { ... }

public Stream<Rs2NpcModel> getStream() { ... }
public Rs2NpcQueryable query() { return new Rs2NpcQueryable(); }
}
```

### Usage Rules

**CRITICAL:** Never instantiate caches or queryables directly. Always access via `Microbot.getRs2XxxCache()`:

```java
// ❌ WRONG - Don't instantiate caches
Rs2NpcCache cache = new Rs2NpcCache();

// ❌ WRONG - Don't instantiate queryables directly
Rs2NpcModel npc = new Rs2NpcQueryable().withName("Banker").nearest();

// ✅ CORRECT - Use Microbot.getRs2XxxCache().query()
Rs2NpcModel npc = Microbot.getRs2NpcCache().query()
.withName("Banker")
.nearest();

// ✅ CORRECT - Other caches
Rs2TileObjectModel tree = Microbot.getRs2TileObjectCache().query().withName("Tree").nearest();
Rs2TileItemModel item = Microbot.getRs2TileItemCache().query().withName("Bones").nearest();
Rs2PlayerModel player = Microbot.getRs2PlayerCache().query().withName("PlayerName").first();

// ✅ CORRECT - Direct stream access
Rs2NpcModel firstNpc = Microbot.getRs2NpcCache().getStream()
.filter(npc -> npc.getName() != null)
.findFirst()
.orElse(null);

// ✅ CORRECT - Boat cache (no queryable, direct methods)
Rs2BoatModel boat = Microbot.getRs2BoatCache().getLocalBoat();
```

### Available Caches

| Cache | Accessor | Methods |
|-------|----------|---------|
| `Rs2NpcCache` | `Microbot.getRs2NpcCache()` | `.query()`, `.getStream()` |
| `Rs2PlayerCache` | `Microbot.getRs2PlayerCache()` | `.query()`, `.getStream()` |
| `Rs2TileItemCache` | `Microbot.getRs2TileItemCache()` | `.query()`, `.getStream()` |
| `Rs2TileObjectCache` | `Microbot.getRs2TileObjectCache()` | `.query()`, `.getStream()` |
| `Rs2BoatCache` | `Microbot.getRs2BoatCache()` | `.getLocalBoat()`, `.getBoat(player)` |

### Boat World View Support
When querying objects on a boat, use `.fromWorldView()` to search the player's current world view:

```java
// Query objects in the boat's world view
var sails = Microbot.getRs2TileObjectCache().query()
.fromWorldView()
.withName("Sails")
.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()`).
- 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.
18 changes: 18 additions & 0 deletions cache/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# cache

What it does: Included build that packages cache tooling/resources required by the RuneLite client (mirrors upstream RuneLite `cache` module).

Public entry points
- Library/tooling jar only; no runtime main.

How to build/test
- From root: `./gradlew -p cache build`
- Included in `./gradlew buildAll`.

Key invariants/constraints
- Must stay version-aligned with `runelite-client` to avoid cache format mismatches.
- Build outputs are consumed during shaded client assembly; avoid breaking task names or published coordinates.

Links
- Architecture: `../docs/ARCHITECTURE.md`
- ADR 0002 (composite build): `../docs/decisions/adr-0002-composite-build-structure.md`
36 changes: 36 additions & 0 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Architecture

## Components & Interaction
- **RuneLite client (`runelite-client`)**: Main application with Microbot hidden plugin (`MicrobotPlugin`) always enabled; entrypoint `net.runelite.client.RuneLite`. Builds shaded distribution via `shadowJar` + `microbotReleaseJar`.
- **Microbot runtime**: `Microbot` singleton exposes caches, utilities, and script lifecycle helpers; `Script` base class drives scheduled script loops; `BlockingEventManager` preempts scripts when required UI/game states are detected.
- **Queryable caches**: Guice-injected caches (`Rs2NpcCache`, `Rs2PlayerCache`, `Rs2TileItemCache`, `Rs2TileObjectCache`, `Rs2BoatCache`, `Rs2PlayerStateCache`) updated per tick and accessed via `Microbot.getRs2XxxCache().query()` or `.getStream()`. World-view aware for boats.
- **Utilities (`microbot/util`)**: Facades over RuneLite APIs for player, inventory, banking, walking, etc.; expected to run on script threads, not the client thread.
- **Included builds**: `runelite-api` (shared API artifacts), `runelite-gradle-plugin` (assemble/index/jarsign tasks), `cache` (cache tooling), `runelite-jshell` (JShell support). Root Gradle orchestrates via composite build.
- **Telemetry/API**: `MicrobotApi` sends session/open/install pings to `https://microbot.cloud/api` using OkHttp; token bundled in code.

## Key Data Flows
- User starts client → `MicrobotClientLoader` fetches jav_config/world info → RuneLite client loads → `MicrobotPlugin` starts, registers overlays/config panels and initializes caches.
- Script loop (`Script.run()` implementations) executes on scheduled executors → queries caches via Queryable API → performs interactions through utilities (`Rs2Inventory`, `Rs2Walker`, etc.) → waits with `sleepUntil` helpers.
- Blocking events (`BlockingEventManager`) continuously validate (e.g., welcome screen, bank popups) → if triggered, they run on a dedicated executor and block script progression until resolved.
- Telemetry flow: `MicrobotApi.microbotOpen/microbotPing/microbotDelete` invoke remote API with session UUID and client version; failures are logged at debug level.

## Runtime Boundaries
- **Threads**: Client thread (never block/sleep); script/executor threads (automation logic, sleeps allowed); blocking-event executor (resolves UI blockers). Use `ClientThread.runOnClientThreadOptional` for safe client access.
- **Services vs libraries**: Microbot plugin runs inside client process; no external services beyond Microbot cloud API; caches are in-memory per client instance.
- **Distribution**: Shaded jar (`*-shaded.jar` and `microbot-<version>.jar`) produced in `runelite-client/build/libs`.

## Configuration & Environments
- Gradle properties: `gradle.properties` holds `microbot.version`, `microbot.commit.sha`, optional repo credentials (`microbot.repo.*`), and `glslang.path` (populated by CI script).
- Runtime config: `MicrobotClientLoader` consumes `RuneLiteProperties` for jav_config URL; falls back to world-supplied hosts on failure. `MicrobotVersionChecker` logs current version/commit on startup.
- Logging: Game chat logging configurable via `MicrobotConfig` (patterns/levels, microbot-only filter); logback appender wired at plugin startup.

## Non-Obvious Behaviors
- Caches refresh at most once per game tick; repeated queries within the same tick reuse cached lists.
- `BlockingEventManager` uses exponential backoff when no events validate to reduce overhead; events are re-queued if execution fails.
- Tests are disabled globally in `runelite-client/build.gradle.kts`; enabling requires flipping `enabled` on `Test` tasks or using `runTests`/`runDebugTests` tasks.
- World hopping short-circuits if the player is interacting or already hopping; confirmation widget auto-click handled in `Microbot.hopToWorld`.

## References
- Queryable API guide: `../runelite-client/src/main/java/net/runelite/client/plugins/microbot/api/QUERYABLE_API.md`
- Development setup: `development.md`
- Decision records: `decisions/`
7 changes: 7 additions & 0 deletions docs/INDEX.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
- [Architecture](ARCHITECTURE.md)
- [Decision Records](decisions/adr-0001-record-architecture-decisions.md)
- [Development Setup](development.md)
- [Installation](installation.md)
- [API Overview](api/README.md)
- [Queryable API Guide](../runelite-client/src/main/java/net/runelite/client/plugins/microbot/api/QUERYABLE_API.md)
- [Gallery](gallery.md)
14 changes: 14 additions & 0 deletions docs/decisions/adr-0001-record-architecture-decisions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# ADR 0001: Record Architecture Decisions

- Status: Accepted (2026-02-08)

## Context
Contributors need a lightweight, traceable way to capture architectural choices across the RuneLite+Microbot composite build without scattering rationale in PRs.

## Decision
Adopt Architecture Decision Records (ADRs) stored under `docs/decisions/` with sequential numbering, concise rationale, and links from `docs/INDEX.md`.

## Consequences
- New significant architectural choices are documented via ADRs.
- Reviewers expect ADR updates when behavior or structure changes at module/runtime boundaries.
- Past rationale is easy to reference for future Codex sessions and human maintainers.
14 changes: 14 additions & 0 deletions docs/decisions/adr-0002-composite-build-structure.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# ADR 0002: Composite Build Structure

- Status: Accepted (2026-02-08)

## Context
The project mirrors upstream RuneLite by using included builds (`cache`, `runelite-api`, `runelite-gradle-plugin`, `runelite-jshell`) plus the `:client` project. Flattening or vendoring these modules would simplify the tree but risk divergence from upstream tooling and artifacts.

## Decision
Keep the composite Gradle setup defined in `settings.gradle.kts`, treating included builds as first-class modules wired via `common.settings.gradle.kts` for shared configuration.

## Consequences
- Contributors work inside the existing multi-project layout; IDEs must import included builds.
- Shared tasks like `buildAll`/`cleanAll` orchestrate across modules; single-module builds remain available for quick iterations (e.g., `:client:compileJava`).
- Dependency version alignment stays consistent with upstream RuneLite release practices.
14 changes: 14 additions & 0 deletions docs/decisions/adr-0003-queryable-cache-pattern.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# ADR 0003: Queryable Cache Pattern for Game Entities

- Status: Accepted (2026-02-08)

## Context
Scripts frequently need fast, thread-safe access to NPCs, players, tile objects/items, and boat state. Direct RuneLite API traversal on script threads is error-prone and can violate client-thread rules.

## Decision
Expose entity data through Guice-managed caches (`Rs2NpcCache`, `Rs2PlayerCache`, `Rs2TileObjectCache`, `Rs2TileItemCache`, `Rs2BoatCache`, `Rs2PlayerStateCache`) accessed only via `Microbot.getRs2XxxCache().query()` or `.getStream()`. Caches refresh per game tick and support world-view queries for boat contexts.

## Consequences
- Scripts must not instantiate caches/queryables directly; tests and new utilities should follow the same pattern.
- Cache access stays tick-consistent and client-thread safe via `ClientThread.runOnClientThreadOptional` wrappers where needed.
- Performance remains predictable by limiting repeated client traversal and encouraging query filters (e.g., `within`, `fromWorldView`).
14 changes: 14 additions & 0 deletions docs/decisions/adr-0004-shaded-distribution-packaging.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# ADR 0004: Shaded Distribution Packaging

- Status: Accepted (2026-02-08)

## Context
End users install Microbot as a single jar. The client depends on multiple modules and native LWJGL artifacts, so an assembled, reproducible output is required. Upstream RuneLite uses shaded packaging; Microbot extends this with versioned release naming.

## Decision
Produce distribution jars via the `shadowJar` task in `runelite-client`, generating both `*-shaded.jar` and `microbot-<microbot.version>.jar` (`microbotReleaseJar`). Keep this as the primary delivery artifact for releases and local testing.

## Consequences
- Build pipelines and docs reference `runelite-client/build/libs/microbot-<version>.jar` as the canonical output.
- Any changes to packaging (signing, classifiers, contents) must update Gradle tasks and release instructions.
- Developers can rely on `./gradlew :client:assemble` to reproduce end-user artifacts without additional tooling.
4 changes: 2 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ org.gradle.parallel=true
org.gradle.caching=false

project.build.group=net.runelite
project.build.version=1.12.16
project.build.version=1.12.17

glslang.path=
microbot.version=2.1.18
microbot.version=2.1.19
microbot.commit.sha=nogit
microbot.repo.url=http://138.201.81.246:8081/repository/microbot-snapshot/
microbot.repo.username=
Expand Down
20 changes: 20 additions & 0 deletions runelite-api/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# runelite-api

What it does: Provides the RuneLite public API artifacts consumed by `runelite-client` and plugins (interfaces, events, data models).

Public entry points
- Library jar only; no runtime main.
- Published via included-build coordinates `net.runelite:runelite-api`.

How to build/test
- From repo root: `./gradlew -p runelite-api build`
- Part of aggregate tasks: `./gradlew buildAll`
- Tests mirror upstream RuneLite defaults.

Key invariants/constraints
- API surface must stay compatible with the client; changes here ripple to `runelite-client` and plugin code.
- Avoid Microbot-specific types creeping into the shared API unless intentionally upstreamed.

Links
- Architecture: `../docs/ARCHITECTURE.md`
- ADR 0002 (composite build): `../docs/decisions/adr-0002-composite-build-structure.md`
16 changes: 8 additions & 8 deletions runelite-api/src/main/interfaces/interfaces.toml
Original file line number Diff line number Diff line change
Expand Up @@ -601,9 +601,9 @@ solved=8
[music]
id=239
container=0
scroll_container=4
list=6
scrollbar=7
scroll_container=8
list=10
scrollbar=11

[nightmare_pillar_health]
id=413
Expand Down Expand Up @@ -785,11 +785,11 @@ init=1

[settings_side]
id=116
camera_zoom_slider_track=57
music_slider=98
music_slider_step_holder=110
sound_effect_slider=112
area_sound_slider=126
camera_zoom_slider_track=56
music_slider=96
music_slider_step_holder=108
sound_effect_slider=110
area_sound_slider=124

[shop]
id=300
Expand Down
1 change: 1 addition & 0 deletions runelite-api/src/main/java/net/runelite/api/NpcID.java
Original file line number Diff line number Diff line change
Expand Up @@ -13018,5 +13018,6 @@ public final class NpcID
public static final int KETZEK_15572 = 15572;
public static final int KETZEK_15573 = 15573;
public static final int TZTOKJAD_15574 = 15574;
public static final int TIGER_SHARK_15575 = 15575;
/* This file is automatically generated. Do not edit. */
}
Loading