> ## Documentation Index
> Fetch the complete documentation index at: https://runelite.zip/llms.txt
> Use this file to discover all available pages before exploring further.

# Events and game data

> Query helpers, event snapshots, utility actions, game vars, ID maps, and progress reads.

Automation reads before it writes. This page collects the read-side surfaces: query helpers for scene and containers, passive event snapshots, raw game variables, revision-aware ID maps, and progress reads.

## Query helpers

Start from a static container and chain filters:

| Entry point                                                      | Query type            |
| ---------------------------------------------------------------- | --------------------- |
| `Inventory.search()`, `Bank.search()`, `BankInventory.search()`  | `ItemQuery`           |
| `Equipment.search()`                                             | `EquipmentItemQuery`  |
| `DepositBox.search()`, `Shop.search()`, `ShopInventory.search()` | `ItemQuery`           |
| `GrandExchangeInventory.search()`                                | `ItemQuery`           |
| `TradeInventory.search(theirs)`                                  | `ItemQuery`           |
| `NPCs.search()`                                                  | `NPCQuery`            |
| `Players.search()`                                               | `PlayerQuery`         |
| `TileObjects.search()`                                           | `TileObjectQuery`     |
| `TileItems.search()`                                             | `TileItemQuery`       |
| `Widgets.search()`                                               | `WidgetQuery`         |
| `Projectiles.search()`                                           | `ProjectileQuery`     |
| `GraphicsObjects.search()`                                       | `GraphicsObjectQuery` |
| `ItemContainers.search(containerId or InventoryID)`              | `ItemContainerQuery`  |

Common filters: `withAnyAction`, `withoutAction`, `nameContainsIgnoreCase`, `withTextContainsIgnoreCase`, `idIn(Collection)`, `withMappedName(String)` (item, NPC, tile objects), `exists`, `count`, `single`, `limit`, `sorted`, `walkable` (tile objects and ground items), and `geTradeable`. `StringMatchers` centralizes tag-stripping and case-insensitive matching.

```java theme={null}
Optional<NPC> banker = NPCs.search()
    .withAnyAction("Bank", "Collect")
    .nameContainsIgnoreCase("banker")
    .nearestByPath();

List<Widget> firstFiveRunes = Bank.search()
    .idIn(Set.of(561, 563, 565))
    .limit(5)
    .result();
```

Rules that matter:

* **Never use `first()` as a nearest-target policy.** Scene collection order is not a distance ranking. Use `walkable().nearestToPoint(anchor)` for a stable work area or `nearestByPath()` for the shortest reachable target from the player.
* **Name predicates strip tags for you.** Inventory and equipment widgets return styled names like `<col=ff9040>Steel axe</col>`; pass bare strings.
* **Mapped names resolve through `IdMapRegistry`.** A mapped base name such as `Ancient sceptre` matches its released variants. Item name filters fall back to the item-definition API when a widget carries only an ID.
* **`geTradeable()` is the market filter.** The older `tradeAble()` remains for source compatibility and delegates to it.
* **`ItemContainers.search(...)` snapshots any container** as immutable `ContainerItem` values. Each call copies live state; take a fresh `search(...)` per independent assertion.
* **`ProjectileQuery` and `GraphicsObjectQuery` snapshot on every `search()`** rather than tick-caching. Reuse one query object to chain filters within a tick.

## Event snapshots

`com.n3plugins.sdk.events` exposes passive, bounded recent-event snapshots. Packet Utils registers `SdkEvents` with the RuneLite event bus during startup and unregisters it at shutdown; consumers read snapshots and never own the bus wiring.

```java theme={null}
SdkEvents.xpGained().forEach(event ->
        log.debug("{} gained {}", event.getSkill(), event.getGainedXp()));

List<NpcLifecycleEvent> bankers = SdkEvents.npcSpawns().stream()
        .filter(event -> "Banker".equals(event.getName()))
        .collect(Collectors.toList());
```

| Snapshot source                                                                 | Contents                                                       |
| ------------------------------------------------------------------------------- | -------------------------------------------------------------- |
| `xpGained()`                                                                    | Skill, previous/current/gained XP, real and boosted level      |
| `inventoryDeltas()` / `inventoryDeltaCursor()` / `inventoryDeltasAfter(cursor)` | Monotonic inventory delta sequences with gap detection         |
| `npcSpawns()` / `npcDespawns()` / `npcLifecycle()`                              | NPC spawn and despawn snapshots with IDs, names, and locations |
| `animationChanges()`                                                            | Actor name, animation ID, locations                            |
| `projectileMovements()`                                                         | Projectile ID, source/target points and actors                 |
| `clear()`                                                                       | Clears retained snapshots and baselines                        |

Snapshots carry client tick and wall-clock timestamps. The tracker retains up to 128 events per kind. Inventory-delta cursors must be persisted once per observation cycle; `isGapDetected()` means output may have been lost and requires resynchronization. Login, hop, and connection loss clear retained item deltas without resetting the sequence, so an old cursor never matches a later session. Unknown containers stay distinct from known-empty ones.

### Scenario and replay foundation

The same package carries the offline simulation foundation used by tests and tooling:

* `EventEnvelope<T>` carries a stable payload, deterministic sequence and tick metadata, and source provenance.
* `StimulusDispatcher` routes envelopes to the first supporting `StimulusHandler` under an explicit `DispatchMode` (`SYNC`, `ASYNC`, `DRY_RUN`), deduplicating by event ID per session.
* `ScenarioRunner` operates tick-driven: dispatch once, then verify state on later ticks. Posting an event never counts as proof the target processed it.
* `EventReplayService` replays a recorded `EventTimeline` through the dispatcher under a `ReplayPolicy`.
* `StateFixtureService` manages named state fixtures with atomic apply/rollback for deterministic scenario setup.

## Utility actions

`UtilityEventActions` collects one-shot utility writes:

| Method                               | Behavior                                                                                                                       |
| ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------ |
| `dropAll(int... itemIds)`            | Drops every matching inventory stack; returns the last successful drop result                                                  |
| `toggleRun()` / `toggleRun(boolean)` | Clicks the run orb. The boolean overload currently dispatches the same toggle because a stable state check is layout-dependent |
| `toggleAcceptAid(boolean)`           | Fails closed with `FAILED`: an explicit unsupported boundary until a stable settings path is verified                          |
| `logout()`                           | Clicks the logout button when visible                                                                                          |

All methods are result-aware. Handle the `toggleAcceptAid` failure explicitly rather than assuming the setting changed.

## Game variables

`GameVars` (`com.n3plugins.sdk.client`) reads raw varbits and varplayers without `Api` dependencies:

```java theme={null}
int stage = GameVars.getVarbit(7334);            // quest stage varbit
boolean autoRetaliate = GameVars.getVarPlayerBit(1, 0);
```

* `getVarbit(id)` and `getVarPlayer(id)` return 0 when the client reads null.
* `getVarPlayerBit(id, bitIndex)` returns false when the client reads null.

Prefer named `net.runelite.api.Varbits` and `VarPlayer` constants as arguments. Revision-pinned named identifiers live in the suite's `GameVarsRegistry`, which DevTools and rune-pouch consumers use instead of local constants classes.

## ID maps

`IdMapRegistry` (`com.n3plugins.sdk.idmaps`) loads generated item, NPC, and object name/ID lookups. Bundled maps ship on the classpath; a complete revision cache under `.runelite/n3Plugins/id-maps/rev-<revision>/` takes precedence when its manifest matches the active client revision.

```java theme={null}
IdMapRegistry maps = IdMapRegistry.getDefault();

Optional<String> itemName = maps.itemName(995);
List<Integer> itemIds = maps.itemIds("Coins");
List<Integer> npcIds = maps.npcIds("'Beedy-eye' Jones");
List<Integer> objectIds = maps.objectIds("4-poster");
```

Query helpers `ItemQuery.withMappedName(...)`, `NPCQuery.withMappedName(...)`, and `TileObjectQuery.withMappedName(...)` resolve IDs through the registry and filter by ID, including RuneLite item variation groups for the current release. Refreshes happen through imports: after generating fresh JSON map files, `IdMapRegistry.importGenerated(...)` validates and copies them into the revision cache with a manifest. No startup scraping or external scripts are involved.

## Progress reads

`com.n3plugins.sdk.progress` provides read-only quest and achievement-diary progress. These APIs read eligibility state; they do not perform quest steps or claim rewards.

### QuestProgressApi

```java theme={null}
QuestState state = QuestProgressApi.state(Quest.COOKS_ASSISTANT);
Optional<Quest> quest = QuestProgressApi.findQuest("Cook's Assistant");
InteractionResult allowed = QuestProgressApi.satisfies(requirement);
```

Methods: `state(quest)`, `isStarted(quest)`, `isComplete(quest)`, `progress(quest or name)`, `all()`, `started()`, `completed()`, `findQuest(name)` (normalized, punctuation-insensitive matching), and `satisfies(requirement)` for walker quest requirements.

### DiaryProgressApi

```java theme={null}
Optional<DiaryProgress> lumbridgeEasy =
        DiaryProgressApi.progress(DiaryRegion.LUMBRIDGE, DiaryTier.EASY);

List<DiaryProgress> varrock = DiaryProgressApi.forRegion(DiaryRegion.VARROCK);
```

Methods: `progress(region, tier)`, `all()`, `forRegion(region)`, `tasks(region, tier)`, `isComplete(task)`, and `progress(task)`. Coverage is intentionally partial: the API exposes only explicitly mapped varbit and varplayer-backed tasks and never guesses unmapped ones.
