Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
444f06e
## Summary
infuse21 Jul 18, 2026
51af334
changed client-thread-guardrail-baseline.txt
infuse21 Jul 18, 2026
82b5377
summary for all current new commits
infuse21 Jul 19, 2026
6c78e87
Updated questhelper to upstream Zoinkwiz/quest-helper commit ae1f4412…
infuse21 Jul 19, 2026
e5eed61
Merge branch 'chsami:main' into QuestHelper
infuse21 Jul 19, 2026
fe77187
regen client-thread-guardrail-baseline.txt
infuse21 Jul 19, 2026
0c95c2d
Merge pull request #6 from infuse21/QuestHelper
infuse21 Jul 20, 2026
8c80d8e
Merge pull request #7 from infuse21/developmentWebWalker
infuse21 Jul 20, 2026
ceed42f
Reconcile shortestpath tracker; set facade-first direction
infuse21 Jul 20, 2026
2119eb9
Stage 1: catalogue full external shortestpath contract
infuse21 Jul 20, 2026
e52b781
Stage 2: add Rs2PathApi facade over shortestpath plugin state
infuse21 Jul 20, 2026
eb0e274
Stage 3a: migrate leaf consumers to Rs2PathApi
infuse21 Jul 20, 2026
6ee086b
Stage 3b: migrate walker-package helpers to Rs2PathApi
infuse21 Jul 20, 2026
e6e7f0c
Stage 3c: migrate Rs2Walker to Rs2PathApi (Stage 3 complete)
infuse21 Jul 20, 2026
2196602
Mark facade migration Stages 1-3 complete in tracker
infuse21 Jul 20, 2026
ad97f1b
Stage 4 audit: correct stale tracker; Tier 1-2 backlog already done
infuse21 Jul 20, 2026
d37a169
Stage 4 re-baseline against current upstream (7e7e5bf94b)
infuse21 Jul 20, 2026
bae1a75
Stage 4 (#3): backfill 12 missing POH portal-chamber destinations
infuse21 Jul 20, 2026
bae5126
Document transports.tsv content diff vs upstream 7e7e5bf94b
infuse21 Jul 20, 2026
f8b1138
Update collision map from upstream (skretzo/master 7e7e5bf94b)
infuse21 Jul 20, 2026
defc219
Backfill 768 missing transports from upstream (7e7e5bf94b)
infuse21 Jul 20, 2026
b42c1f8
Doc: mark transports import + collision map done in comparison
infuse21 Jul 20, 2026
3149b6b
Reconcile stale 07fca57 sections in UPSTREAM_COMPARISON
infuse21 Jul 20, 2026
7e1239a
Backfill Cowbell Amulet, Sailors' Amulet, Laguna Aurorae from upstream
infuse21 Jul 20, 2026
d10aee8
Doc: mark Cowbell/Sailors'/Laguna Aurorae done in comparison table
infuse21 Jul 20, 2026
e31944c
Fix walker route-click selection: line-of-sight clamping and anchor r…
infuse21 Jul 21, 2026
9c378d3
Attribute per-handler cost in the raw-path scene scan
infuse21 Jul 21, 2026
651dc5e
Snapshot the scene once per raw-path scan instead of querying per probe
infuse21 Jul 21, 2026
db9e191
Attribute transport-refresh cache misses and slow stages
infuse21 Jul 21, 2026
ce268fd
Hash only transport-gating skills in the transport refresh verification
infuse21 Jul 21, 2026
8aee47d
Compute the LOS regression route once per class
infuse21 Jul 21, 2026
337d51d
Suppress the inverse adjacent transport when the landing check fails
infuse21 Jul 21, 2026
a407210
Hash varplayer condition verdicts instead of raw values
infuse21 Jul 21, 2026
d58ab1a
Hash varbit condition verdicts instead of raw values
infuse21 Jul 21, 2026
038c9e8
Revert line-of-sight click clamping; keep clicks on the raw route
infuse21 Jul 21, 2026
2701c23
Correct movement.md #19 after reverting reachability-gated primary se…
infuse21 Jul 21, 2026
1121e62
Audit movement.md gotchas: cross-reference #5 and add a reading guide
infuse21 Jul 21, 2026
2892605
Jitter route click reach so clicks do not all cover the same tile span
infuse21 Jul 21, 2026
95e68a0
Apply route click reach jitter on the player-anchored retry too
infuse21 Jul 21, 2026
d7bef88
Correct Rs2PathApi migration status: Stage 3 is done
infuse21 Jul 21, 2026
89c5df9
Do not spend partial retries on iterations that made progress
infuse21 Jul 21, 2026
6b8d9ca
Require movement, not just a route change, to refill partial retries
infuse21 Jul 21, 2026
d16e9d1
Reject walk targets that are not walkable terrain, before routing
infuse21 Jul 21, 2026
e865551
Gate rockfall handling on the rockfall, not on the walk target
infuse21 Jul 21, 2026
0f91de0
Add GET /objects/neighbours for cataloguing between-tile obstacles
infuse21 Jul 21, 2026
f9aef42
Catalogue Motherlode Mine rockfalls as transports
infuse21 Jul 21, 2026
6415b8f
Let item-gated plain transports take part in bank planning
infuse21 Jul 21, 2026
5c06b86
Revert "Reject walk targets that are not walkable terrain, before rou…
infuse21 Jul 21, 2026
289090d
Revert "Catalogue Motherlode Mine rockfalls as transports"
infuse21 Jul 21, 2026
cf8cf03
Restore Motherlode Mine collision region from the 11/19 cache
infuse21 Jul 21, 2026
c626321
Reapply "Reject walk targets that are not walkable terrain, before ro…
infuse21 Jul 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
46 changes: 46 additions & 0 deletions docs/AGENT_SERVER.md
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,52 @@ Body: `{"name": "Banker", "action": "Bank"}` or `{"id": 3010, "action": "Attack"
}
```

#### GET /objects/neighbours

Same filters as `GET /objects`, plus the walkability of the tiles surrounding each match. Use it to
catalogue obstacles that sit *between* two walkable tiles — Motherlode rockfalls, rubble, rock
slides — where modelling the obstacle as a transport in `transports.tsv` needs both of its open
ends, which the object's own position does not give you.

| Parameter | Default | Description |
|-----------|---------|-------------|
| `id` / `name` / `nameContains` | - | Same filters as `GET /objects` |
| `maxDistance` | `20` | Max tile distance (scene radius is ~104) |
| `radius` | `1` | Neighbour ring size, clamped to 1-5 |
| `limit` | config max | Max results |

`sides` pairs opposing neighbours on each axis, so a blocking obstacle's two endpoints can be read
straight off the response. Walkability comes from the **live scene** collision flags, not the
bundled collision map, so it reflects what the player can actually do right now — which is the
point when the bundled map has an obstacle baked in as a permanent wall.

```bash
curl -s -H "X-Agent-Token: $(cat ~/.runelite/.agent-token)" \
"http://127.0.0.1:8081/objects/neighbours?id=26679&maxDistance=104&limit=200"
```

```json
{
"count": 1, "total": 1, "radius": 1,
"objects": [
{
"id": 26679, "name": "Rockfall", "type": "GAME", "reachable": true,
"position": {"x": 3742, "y": 5668, "plane": 0},
"selfWalkable": false,
"neighbours": [
{"position": {"x": 3741, "y": 5668, "plane": 0}, "dx": -1, "dy": 0, "walkable": true}
],
"sides": {
"eastWest": {"a": {"...": "dx=-1"}, "b": {"...": "dx=+1"}, "bothWalkable": true},
"northSouth": {"a": {"...": "dy=-1"}, "b": {"...": "dy=+1"}, "bothWalkable": false}
}
}
]
}
```

The axis whose `bothWalkable` is `true` gives the transport's origin/destination pair.

#### POST /objects/interact

Body: `{"name": "Oak tree", "action": "Chop down"}`
Expand Down
302 changes: 295 additions & 7 deletions docs/entity-guides/movement.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.coords.WorldPoint;
import net.runelite.client.plugins.microbot.shortestpath.ShortestPathPlugin;
import net.runelite.client.plugins.microbot.util.walker.Rs2PathApi;
import net.runelite.client.plugins.microbot.util.Global;
import net.runelite.client.plugins.microbot.agentserver.handler.ScriptHeartbeatRegistry;
import net.runelite.client.plugins.microbot.util.antiban.SessionFatigue;
Expand Down Expand Up @@ -56,7 +56,7 @@ public void shutdown() {
ScriptHeartbeatRegistry.remove(this.getClass().getName());
if (mainScheduledFuture != null && !mainScheduledFuture.isDone()) {
mainScheduledFuture.cancel(true);
ShortestPathPlugin.exit();
Rs2PathApi.exit();
if (Microbot.getClientThread().scheduledFuture != null)
Microbot.getClientThread().scheduledFuture.cancel(true);
initialPlayerLocation = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ protected void handleRequest(HttpExchange exchange) throws IOException {
case "/interact":
handleInteract(exchange);
break;
case "/neighbours":
case "/neighbors":
handleNeighbours(exchange);
break;
default:
sendJson(exchange, 404, errorResponse("Unknown path: /objects" + sub));
}
Expand Down Expand Up @@ -133,6 +137,147 @@ private void handleInteract(HttpExchange exchange) throws IOException {
sendJson(exchange, 200, response);
}

/**
* Like the plain query, but also reports the walkability of the tiles surrounding each match.
*
* <p>Exists to catalogue obstacles that sit <em>between</em> two walkable tiles — Motherlode
* rockfalls, rubble, rock slides. Modelling one as a transport needs both sides, which the
* object's own position does not give you. {@code sides} pairs opposing neighbours on each axis
* so a blocking obstacle's two open ends can be read straight off the response.
*
* <p>Walkability comes from the <em>live scene</em> collision flags, not the bundled collision
* map, so it reflects what the player can actually do right now. The flag array is read once per
* request rather than per tile, since each {@code Rs2Tile.isWalkable} call hops the client thread.
*/
private void handleNeighbours(HttpExchange exchange) throws IOException {
try {
requireGet(exchange);
} catch (HttpMethodException e) {
sendJson(exchange, 405, errorResponse(e.getMessage()));
return;
}

Map<String, String> params = parseQuery(exchange.getRequestURI());
String name = params.get("name");
String nameContains = params.get("nameContains");
int id = getIntParam(params, "id", -1);
int maxDistance = getIntParam(params, "maxDistance", 20);
int limit = getIntParam(params, "limit", defaultLimit);
int radius = Math.max(1, Math.min(5, getIntParam(params, "radius", 1)));

var query = Microbot.getRs2TileObjectCache().query();
if (id >= 0) {
query = query.withId(id);
}
if (name != null && !name.isEmpty()) {
query = query.withName(name);
} else if (nameContains != null && !nameContains.isEmpty()) {
query = query.withNameContains(nameContains);
}
query = query.within(maxDistance);

List<Rs2TileObjectModel> objects = query.toListOnClientThread();
List<Rs2TileObjectModel> limited = objects.stream().limit(limit).collect(Collectors.toList());

List<Map<String, Object>> serialized = Microbot.getClientThread().runOnClientThreadOptional(() -> {
var wv = Microbot.getClient().getTopLevelWorldView();
if (wv == null) {
return null;
}
var collisionData = wv.getCollisionMaps();
int[][] flags = collisionData != null ? collisionData[wv.getPlane()].getFlags() : null;

List<Map<String, Object>> out = new ArrayList<>();
for (Rs2TileObjectModel obj : limited) {
Map<String, Object> map = serializeObject(obj);
WorldPoint loc = obj.getWorldLocation();
if (loc == null) {
out.add(map);
continue;
}

Map<String, Map<String, Object>> byOffset = new LinkedHashMap<>();
List<Map<String, Object>> neighbours = new ArrayList<>();
for (int dx = -radius; dx <= radius; dx++) {
for (int dy = -radius; dy <= radius; dy++) {
if (dx == 0 && dy == 0) {
continue;
}
WorldPoint p = new WorldPoint(loc.getX() + dx, loc.getY() + dy, loc.getPlane());
Map<String, Object> entry = new LinkedHashMap<>();
entry.put("position", positionOf(p));
entry.put("dx", dx);
entry.put("dy", dy);
entry.put("walkable", isWalkableInScene(wv, flags, p));
neighbours.add(entry);
byOffset.put(dx + "," + dy, entry);
}
}
map.put("neighbours", neighbours);

// Opposing pairs on each axis: for an obstacle standing between two open tiles these
// are the transport's two endpoints.
Map<String, Object> sides = new LinkedHashMap<>();
sides.put("eastWest", axisPair(byOffset, -1, 0, 1, 0));
sides.put("northSouth", axisPair(byOffset, 0, -1, 0, 1));
map.put("sides", sides);

map.put("selfWalkable", isWalkableInScene(wv, flags, loc));
out.add(map);
}
return out;
}).orElse(null);

if (serialized == null) {
sendJson(exchange, 503, errorResponse("Scene unavailable (not logged in?)"));
return;
}

Map<String, Object> response = new LinkedHashMap<>();
response.put("count", serialized.size());
response.put("total", objects.size());
response.put("radius", radius);
response.put("objects", serialized);
sendJson(exchange, 200, response);
}

private static Map<String, Object> axisPair(Map<String, Map<String, Object>> byOffset,
int aDx, int aDy, int bDx, int bDy) {
Map<String, Object> a = byOffset.get(aDx + "," + aDy);
Map<String, Object> b = byOffset.get(bDx + "," + bDy);
Map<String, Object> pair = new LinkedHashMap<>();
pair.put("a", a);
pair.put("b", b);
pair.put("bothWalkable", a != null && b != null
&& Boolean.TRUE.equals(a.get("walkable")) && Boolean.TRUE.equals(b.get("walkable")));
return pair;
}

private static Map<String, Integer> positionOf(WorldPoint p) {
Map<String, Integer> position = new LinkedHashMap<>();
position.put("x", p.getX());
position.put("y", p.getY());
position.put("plane", p.getPlane());
return position;
}

/** Live-scene walkability; false when the tile is outside the loaded scene. */
private static boolean isWalkableInScene(net.runelite.api.WorldView wv, int[][] flags, WorldPoint p) {
if (flags == null) {
return false;
}
net.runelite.api.coords.LocalPoint lp = net.runelite.api.coords.LocalPoint.fromWorld(wv, p);
if (lp == null) {
return false;
}
int sx = lp.getSceneX();
int sy = lp.getSceneY();
if (sx < 0 || sy < 0 || sx >= flags.length || sy >= flags[sx].length) {
return false;
}
return (flags[sx][sy] & net.runelite.api.CollisionDataFlag.BLOCK_MOVEMENT_FULL) == 0;
}

private Map<String, Object> serializeObject(Rs2TileObjectModel obj) {
Map<String, Object> map = new LinkedHashMap<>();
map.put("id", obj.getId());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,22 @@
# Use the quest-helper update scripts to manage synchronization.

# Current sync position - this is the last external commit that has been integrated
5539626281a502ba8c44fe59b8b57bce32fe4f16
ae1f441248b9742c1eaa667b18052785eb6ed058

# Sync metadata (for script reference)
# External repo HEAD at last check: 5539626281a502ba8c44fe59b8b57bce32fe4f16
# Last sync date: 2025-08-29
# Sync method: automated script with manual package transformation
# Integration branch: quest-helper-update-20250829_063754
# Local commit: cb416ef02a (quest-helper: polish Enakhras Lament)
# External repo HEAD at last check: ae1f441248b9742c1eaa667b18052785eb6ed058
# Last sync date: 2026-07-19
# Sync method: upstream source mirror with manual package transformation and Microbot hook preservation
# Integration branch: local workspace
# Local commit: pending

# Sync history log:
# 2026-07-19 - Complete sync to upstream ae1f4412 (Quest Helper 4.16.1 source)
# - Mirrored upstream src/main/java and src/main/resources into the Microbot package
# - Preserved Microbot QuestScript, custom quest logic, config toggles, and agent-server start hook
# - Package transformation: com.questhelper -> net.runelite.client.plugins.microbot.questhelper
# - Status: SUCCESS
#
# 2025-08-29 07:14:46 - Applied commit 55396262 (Polish enahkras lament #2294)
# - Enhanced EnakhrasLament.java with better code organization
# - Fixed Shadow Room brazier puzzle sequence and locations
Expand All @@ -43,9 +49,9 @@
# - Status: INITIALIZED

# Integration status
# Current external version: 4.10.0+
# Current external version: 4.16.1
# Integration health: HEALTHY
# Last validation: 2025-08-29
# Last validation: 2026-07-19
# Compilation status: SUCCESS
# Package transformation: COMPLETE
# Sync file location: CORRECT (moved from project root)
# Sync file location: CORRECT (moved from project root)
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ public interface QuestHelperConfig extends Config
String QUEST_HELPER_GROUP = "questhelper";
String QUEST_BACKGROUND_GROUP = "questhelpervars";
String QUEST_HELPER_SIDEBAR_ORDER_KEY_START = "quest-sidebar-order-";
/** Per-helper persisted manual step skips (JSON map of stable slot id to true). */
String QUEST_HELPER_MANUAL_SKIPS_KEY_PREFIX = "manual-step-skips-";

enum QuestOrdering implements Comparator<QuestHelper>
{
Expand Down Expand Up @@ -237,32 +239,33 @@ enum InventoryItemHighlightStyle
}

@ConfigSection(
position = 0,
name = "Microbot",
description = "Microbot Options",
closedByDefault = false
position = 0,
name = "Microbot",
description = "Microbot Options",
closedByDefault = false
)
String microbotSection = "microbotSection";


@ConfigItem(
keyName = "TurnOn",
name = "Enable QuestHelper",
description = "Enable the quest helper that will automatically help with quests.",
section = microbotSection
keyName = "TurnOn",
name = "Enable QuestHelper",
description = "Enable the quest helper that will automatically help with quests.",
section = microbotSection
)
default boolean startStopQuestHelper() {
default boolean startStopQuestHelper()
{
return true;
}

@ConfigItem(
keyName = "obtainMissingItems",
name = "Obtain Missing Items",
description = "Controls whether the quest helper automatically obtains missing items from the bank and Grand Exchange. 'Ask' will prompt you the first time items are needed.",
section = microbotSection,
position = 1
keyName = "obtainMissingItems",
name = "Obtain Missing Items",
description = "Controls whether the quest helper automatically obtains missing items from the bank and Grand Exchange. 'Ask' will prompt you the first time items are needed.",
section = microbotSection,
position = 1
)
default ObtainMissingItemsOption obtainMissingItems() {
default ObtainMissingItemsOption obtainMissingItems()
{
return ObtainMissingItemsOption.ASK;
}

Expand Down Expand Up @@ -332,14 +335,15 @@ public String toString()
}

@ConfigItem(
keyName = "valeTotemsWoodType",
name = "Vale Totems wood type",
description = "Which wood type the quester should gather for the Vale Totems miniquest. " +
"Must match the wood you used to build the totem. " +
"Leave on 'Ask me' to be prompted the first time the quest runs.",
section = microbotSection
keyName = "valeTotemsWoodType",
name = "Vale Totems wood type",
description = "Which wood type the quester should gather for the Vale Totems miniquest. " +
"Must match the wood you used to build the totem. " +
"Leave on 'Ask me' to be prompted the first time the quest runs.",
section = microbotSection
)
default ValeTotemsWoodType valeTotemsWoodType() {
default ValeTotemsWoodType valeTotemsWoodType()
{
return ValeTotemsWoodType.ASK;
}

Expand Down Expand Up @@ -791,6 +795,25 @@ default boolean showCompletedQuests()
return false;
}

enum RegionFilterVisibility
{
AUTO,
SHOW,
HIDE
}

@ConfigItem(
keyName = "regionFilterVisibility",
name = "Region filtering",
description = "Controls when the league region filter is shown. Auto shows it only on league worlds.",
position = 5,
section = filterSection
)
default RegionFilterVisibility regionFilterVisibility()
{
return RegionFilterVisibility.AUTO;
}

@ConfigSection(
position = 5,
name = "Development",
Expand Down
Loading
Loading