Queryable API

A fluent interface for querying game entities.

The Queryable API provides a type-safe, chainable way to find NPCs, players, ground items, and tile objects. Access the cache via Microbot.getRs2XxxCache(), call query(), chain filters, and execute.

📚 Complete Documentation: See api/QUERYABLE_API.md for comprehensive guide

Quick Example

// Access cache via Microbot static getter
Rs2NpcModel banker = Microbot.getRs2NpcCache().query()
    .withName("Banker")
    .where(npc -> !npc.isInteracting())
    .nearestOnClientThread();

Singleton Architecture

All caches are @Singleton classes injected via Guice and exposed through Microbot static getters.

Cache Structure

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

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

Each cache provides:

getStream() - Direct access to the underlying stream
query() - Returns a fluent queryable interface for filtering

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)

Usage Rules

⚠️ CRITICAL

Never instantiate caches or queryables directly. Always access via Microbot.getRs2XxxCache().

✅ Correct Usage Pattern

// ✅ CORRECT - Use cache.query() method
Rs2NpcModel banker = Microbot.getRs2NpcCache().query()
    .withName("Banker")
    .where(npc -> !npc.isInteracting())
    .nearestOnClientThread();

// ✅ CORRECT - Ground items via cache
Rs2TileItemModel loot = Microbot.getRs2TileItemCache().query()
    .withName("Dragon bones")
    .where(item -> item.getValue() > 1000)
    .nearestOnClientThread();

// ✅ CORRECT - Tile objects via cache
Rs2TileObjectModel tree = Microbot.getRs2TileObjectCache().query()
    .withName("Oak tree")
    .nearestOnClientThread();
tree.click("Chop down");

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

// ✅ CORRECT - Boat cache (direct methods, no queryable)
Rs2BoatModel boat = Microbot.getRs2BoatCache().getLocalBoat();
if (boat != null) {
    boat.trimSails();
    boat.navigate();
}

❌ Wrong Usage

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

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

// ❌ WRONG - Don't use deprecated static methods
Rs2NpcModel npc = Rs2NpcCache.getNpcsStream().findFirst().orElse(null);

Rs2NpcCache

Query NPCs in the game world

// Find nearest banker (uses client thread for name query)
Rs2NpcModel banker = Microbot.getRs2NpcCache().query()
    .withName("Banker")
    .nearestOnClientThread();

// Find all guards within 10 tiles
List<Rs2NpcModel> guards = Microbot.getRs2NpcCache().query()
    .withName("Guard")
    .within(10)
    .toList();

// Find attackable enemy
Rs2NpcModel enemy = Microbot.getRs2NpcCache().query()
    .withName("Goblin")
    .where(npc -> !npc.isInteracting())
    .where(npc -> npc.getHealthRatio() > 0)
    .nearestOnClientThread();

Rs2NpcModel Methods

npc.getName() npc.getId() npc.getWorldLocation() npc.isInteracting() npc.getHealthRatio() npc.getAnimation() npc.click("Action") npc.interact("Action")

Rs2TileItemCache

Query ground items

// Find nearest coins (uses client thread for name query)
Rs2TileItemModel coins = Microbot.getRs2TileItemCache().query()
    .withName("Coins")
    .nearestOnClientThread();

// Find valuable loot (no name query, no client thread needed)
Rs2TileItemModel loot = Microbot.getRs2TileItemCache().query()
    .where(item -> item.getTotalGeValue() >= 5000)
    .where(Rs2TileItemModel::isLootAble)
    .nearest(10);

if (loot != null) {
    loot.pickup();
}

Rs2TileItemModel Methods

item.getName() item.getId() item.getQuantity() item.getTotalValue() item.getTotalGeValue() item.isLootAble() item.isStackable() item.pickup()

Rs2PlayerCache

Query other players

// Find nearest player (no name query, no client thread needed)
Rs2PlayerModel player = Microbot.getRs2PlayerCache().query().nearest();

// Find player by name (uses client thread for name query)
Rs2PlayerModel target = Microbot.getRs2PlayerCache().query()
    .withName("PlayerName")
    .nearestOnClientThread();

// Find all friends nearby (no name query, no client thread needed)
List<Rs2PlayerModel> friends = Microbot.getRs2PlayerCache().query()
    .where(Rs2PlayerModel::isFriend)
    .within(20)
    .toList();

Rs2PlayerModel Methods

player.getName() player.getCombatLevel() player.getHealthRatio() player.isFriend() player.isClanMember() player.isInteracting()

Rs2TileObjectCache

Query game objects (trees, rocks, banks, etc.)

// Find nearest tree (uses client thread for name query)
Rs2TileObjectModel tree = Microbot.getRs2TileObjectCache().query()
    .withName("Tree")
    .nearestOnClientThread();

// Find nearest bank (uses client thread for name query)
Rs2TileObjectModel bank = Microbot.getRs2TileObjectCache().query()
    .withNames("Bank booth", "Bank chest", "Bank")
    .nearestOnClientThread();

if (bank != null && !Rs2Bank.isOpen()) {
    bank.click("Bank");
}

Rs2TileObjectModel Methods

obj.getName() obj.getId() obj.getWorldLocation() obj.click("Action") obj.interact("Action") obj.isReachable()

Rs2BoatCache

Access boat information (Sailing content)

Note: Rs2BoatCache provides direct methods instead of a queryable interface.

// Get the local player's boat
Rs2BoatModel boat = Microbot.getRs2BoatCache().getLocalBoat();

// Get another player's boat
Rs2BoatModel otherBoat = Microbot.getRs2BoatCache().getBoat(player);

if (boat != null) {
    boat.trimSails();
    boat.navigate();
    
    // Check boat state
    boolean isNavigating = boat.isNavigating();
}

Rs2BoatCache Methods

getLocalBoat() getBoat(Player player)

Rs2BoatModel Methods

boat.trimSails() boat.navigate() boat.isNavigating()

API Reference

Terminal Operations

Execute the query and return results:

nearest()

Returns nearest entity to player

nearest(int maxDistance)

Returns nearest within max distance

first()

Returns first matching entity

firstOnClientThread()

Returns first entity (client thread safe). Required for name queries.

nearestOnClientThread()

Returns nearest entity (client thread safe). Required for name queries.

withName(String name)

Find nearest with exact name. ⚠️ Use with nearestOnClientThread()

withNames(String... names)

Find nearest matching any name. ⚠️ Use with nearestOnClientThread()

withId(int id)

Find nearest with specific ID

toList()

Returns all matching entities

Filter Operations

Filter entities and return queryable for chaining:

where(Predicate<E> predicate)

Add custom filter with lambda

within(int distance)

Filter within distance from player

within(WorldPoint anchor, int distance)

Filter within distance from point

Threading

Scripts run on a scheduled executor thread, but certain RuneLite API calls must run on the client thread.

⚠️ Client Thread Required

Queries using withName() or withNames() access widget data. Use nearestOnClientThread() or firstOnClientThread() instead of nearest() or first().

Name Queries

// ❌ WRONG - May throw "must be called on client thread" error
Rs2NpcModel banker = Microbot.getRs2NpcCache().query()
    .withName("Banker")
    .nearest();

// ✅ CORRECT - Runs the query on the client thread
Rs2NpcModel banker = Microbot.getRs2NpcCache().query()
    .withName("Banker")
    .nearestOnClientThread();

// ✅ CORRECT - Using first variant
Rs2NpcModel banker = Microbot.getRs2NpcCache().query()
    .withName("Banker")
    .firstOnClientThread();

Manual Client Thread Invocation

For complex scenarios or multiple client-thread operations:

// For operations that return a value
Rs2NpcModel banker = Microbot.getClientThread().invoke(() -> 
    Microbot.getRs2NpcCache().query()
        .withName("Banker")
        .nearest()
);

// For void operations
Microbot.getClientThread().invoke(() -> {
    Rs2NpcModel npc = Microbot.getRs2NpcCache().query().withName("Guard").nearest();
    if (npc != null) {
        npc.click("Attack");
    }
});

Other Client Thread Operations

Always use Microbot.getClientThread().invoke() when accessing:

client.getWidget() widget.isHidden() client.getVarbitValue() client.getLocalPlayer().getWorldView() BoatLocation.fromLocal() TrialInfo.getCurrent() Rs2BoatCache.getLocalBoat() Rs2BoatModel.isNavigating()

Boat World View Support

When on a boat, objects exist in a different world view. Use .fromWorldView() to query the player's current world view.

// Query objects in the boat's world view
var sails = Microbot.getRs2TileObjectCache().query()
    .fromWorldView()
    .withName("Sails")
    .nearestOnClientThread();

// Reachability is automatically handled for same world view
if (sails != null && sails.isReachable()) {
    sails.click("Trim");
}

Tip: The .fromWorldView() method automatically uses the local player's current world view, which is essential when on boats or in other instanced areas.

Common Patterns

Combat Script

Rs2NpcModel enemy = Microbot.getRs2NpcCache().query()
    .withName("Goblin")
    .where(npc -> !npc.isInteracting())
    .where(npc -> npc.getHealthRatio() > 0)
    .nearestOnClientThread();

if (enemy != null && !Rs2Player.isInCombat()) {
    enemy.click("Attack");
}

Looting Script

Rs2TileItemModel loot = Microbot.getRs2TileItemCache().query()
    .where(Rs2TileItemModel::isLootAble)
    .where(item -> item.getTotalGeValue() >= 5000)
    .nearest(15);  // No name query, no client thread needed

if (loot != null) {
    loot.pickup();
}

Null Safety

Rs2NpcModel banker = Microbot.getRs2NpcCache().query()
    .withName("Banker")
    .nearestOnClientThread();

if (banker != null) {
    banker.click("Bank");
}

Reusable Predicates

Predicate<Rs2NpcModel> isAlive = npc -> npc.getHealthRatio() > 0;
Predicate<Rs2NpcModel> notBusy = npc -> !npc.isInteracting();

Rs2NpcModel target = Microbot.getRs2NpcCache().query()
    .withName("Cow")
    .where(isAlive)
    .where(notBusy)
    .nearestOnClientThread();

Migration Guide

Migrating from the legacy API:

NPCs

Legacy

NPC npc = Rs2Npc.getNpc("Banker");
NPC nearest = Rs2Npc.getNearestNpc("Guard");

Queryable

Rs2NpcModel npc = Microbot.getRs2NpcCache().query()
    .withName("Banker")
    .nearestOnClientThread();
Rs2NpcModel nearest = Microbot.getRs2NpcCache().query()
    .withName("Guard")
    .nearestOnClientThread();

Ground Items

Legacy

TileItem item = Rs2GroundItem.findItem("Coins");

Queryable

Rs2TileItemModel item = Microbot.getRs2TileItemCache().query()
    .withName("Coins")
    .firstOnClientThread();

Interactions

Legacy

Rs2Npc.interact(banker, "Bank");

Queryable

banker.click("Bank");

Legacy Direct Cache Access (Deprecated)

⚠️ Deprecated

Static methods are deprecated and will be removed in future versions. Use Microbot.getRs2XxxCache().getStream() instead.

// ⚠️ DEPRECATED - Static methods are deprecated and will be removed
// Use Microbot.getRs2XxxCache().getStream() instead

Rs2NpcCache.getNpcsStream()           // deprecated → Microbot.getRs2NpcCache().getStream()
Rs2PlayerCache.getPlayersStream()     // deprecated → Microbot.getRs2PlayerCache().getStream()
Rs2TileItemCache.getTileItemsStream() // deprecated → Microbot.getRs2TileItemCache().getStream()
Rs2TileObjectCache.getObjectsStream() // deprecated → Microbot.getRs2TileObjectCache().getStream()

Migration from Deprecated APIs

Deprecated

Rs2NpcCache.getNpcsStream()
    .filter(npc -> npc.getName() != null)
    .findFirst()
    .orElse(null);

New API

Microbot.getRs2NpcCache().getStream()
    .filter(npc -> npc.getName() != null)
    .findFirst()
    .orElse(null);