fix: block Al Kharid mine in pathfinder collision data#1813
Conversation
The shortest-path A* pathfinder routes through the Al Kharid mine open pit because the cache-derived collision data doesn't mark the terrain as blocking. This causes the walker to get stuck when the path enters the mine area. Add blocked edges around the mine perimeter (x=3281-3300, y=3151-3178) to prevent the pathfinder from routing through it.
WalkthroughThis PR updates the RuneLite client dependency to a new game version. It changes camera focal point/pitch/yaw API types and units, removes Changes
Sequence Diagram(s)sequenceDiagram
participant MenuEntrySwapperPlugin
participant EntityOps
MenuEntrySwapperPlugin->>EntityOps: getSubOps(opIdx)
EntityOps-->>MenuEntrySwapperPlugin: subops
MenuEntrySwapperPlugin->>EntityOps: subops.getNumOps()
MenuEntrySwapperPlugin->>EntityOps: subops.getOp(subIdx)
MenuEntrySwapperPlugin->>MenuEntrySwapperPlugin: packSubID(opIdx, subIdx)
Possibly related PRs
Suggested labels: dependencies, cache, api-change, generated-ids Suggested reviewers: chsami I combed through the sub-op refactor and the WorldMapManager named/ID fallback — logic checks out consistently. No inconsistencies spotted worth flagging! 🐰
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches⚔️ Resolve merge conflicts
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ast-grep (0.44.0)runelite-client/src/main/java/net/runelite/client/plugins/config/PluginHubPanel.java[{"text":""https://runelite.net/plugin-hub/show/" + manifest.getInternalName()","range":{"byteOffset":{"start":9678,"end":9746},"start":{"line":310,"column":51},"end":{"line":310,"column":119}},"file":"runelite-client/src/main/java/net/runelite/client/plugins/config/PluginHubPanel.java","lines":"\t\t\thelp.addActionListener(ev -> LinkBrowser.browse("https://runelite.net/plugin-hub/show/" + manifest.getInternalName()));","charCount":{"leading":51,"trailing":3},"language":"Java","metaVariables":{"single":{},"multi":{"secondary":[{"text":""https://runelite.net/plugin-hub/show/"","range":{"byteOffset":{"start":9678,"end":9717},"start":{"line":310,"column":51},"end":{"line":310,"column":90}}}]},"transformed":{}},"ruleId":"tainted-url-host","severity":"warning","note":"[CWE-20] Improper Input Validation. [REFERENCES]\n - https://cwe.mitre.org/data/definitions/20.html","message":"Avoid building a URL host from untrusted input","labels":[{"text":""https://runelite.net/plugin-hub/show ... [truncated 893 characters] ... mn":44}}},"FILTER":{"text":"query","range":{"byteOffset":{"start":21956,"end":21961},"start":{"line":700,"column":46},"end":{"line":700,"column":51}}},"CTX":{"text":"PluginSearch","range":{"byteOffset":{"start":21927,"end":21939},"start":{"line":700,"column":17},"end":{"line":700,"column":29}}}},"multi":{},"transformed":{}},"ruleId":"ldap-injection-java","severity":"warning","note":"[CWE-90] Improper Neutralization of Special Elements used in an LDAP Query ('LDAP Injection'). Security best practice. [REFERENCES]\n - https://cwe.mitre.org/data/definitions/90.html","message":"Avoid LDAP injections","labels":[{"text":"PluginSearch.search(plugins, query)","range":{"byteOffset":{"start":21927,"end":21962},"start":{"line":700,"column":17},"end":{"line":700,"column":52}},"style":"primary"}]}] runelite-client/src/main/java/net/runelite/client/plugins/gpu/SceneUploader.java[{"text":""https://runelite.net/plugin-hub/show/" + manifest.getInternalName()","range":{"byteOffset":{"start":9678,"end":9746},"start":{"line":310,"column":51},"end":{"line":310,"column":119}},"file":"runelite-client/src/main/java/net/runelite/client/plugins/config/PluginHubPanel.java","lines":"\t\t\thelp.addActionListener(ev -> LinkBrowser.browse("https://runelite.net/plugin-hub/show/" + manifest.getInternalName()));","charCount":{"leading":51,"trailing":3},"language":"Java","metaVariables":{"single":{},"multi":{"secondary":[{"text":""https://runelite.net/plugin-hub/show/"","range":{"byteOffset":{"start":9678,"end":9717},"start":{"line":310,"column":51},"end":{"line":310,"column":90}}}]},"transformed":{}},"ruleId":"tainted-url-host","severity":"warning","note":"[CWE-20] Improper Input Validation. [REFERENCES]\n - https://cwe.mitre.org/data/definitions/20.html","message":"Avoid building a URL host from untrusted input","labels":[{"text":""https://runelite.net/plugin-hub/show ... [truncated 893 characters] ... mn":44}}},"FILTER":{"text":"query","range":{"byteOffset":{"start":21956,"end":21961},"start":{"line":700,"column":46},"end":{"line":700,"column":51}}},"CTX":{"text":"PluginSearch","range":{"byteOffset":{"start":21927,"end":21939},"start":{"line":700,"column":17},"end":{"line":700,"column":29}}}},"multi":{},"transformed":{}},"ruleId":"ldap-injection-java","severity":"warning","note":"[CWE-90] Improper Neutralization of Special Elements used in an LDAP Query ('LDAP Injection'). Security best practice. [REFERENCES]\n - https://cwe.mitre.org/data/definitions/90.html","message":"Avoid LDAP injections","labels":[{"text":"PluginSearch.search(plugins, query)","range":{"byteOffset":{"start":21927,"end":21962},"start":{"line":700,"column":17},"end":{"line":700,"column":52}},"style":"primary"}]}] runelite-client/src/main/java/net/runelite/client/plugins/gpu/Zone.java[{"text":""https://runelite.net/plugin-hub/show/" + manifest.getInternalName()","range":{"byteOffset":{"start":9678,"end":9746},"start":{"line":310,"column":51},"end":{"line":310,"column":119}},"file":"runelite-client/src/main/java/net/runelite/client/plugins/config/PluginHubPanel.java","lines":"\t\t\thelp.addActionListener(ev -> LinkBrowser.browse("https://runelite.net/plugin-hub/show/" + manifest.getInternalName()));","charCount":{"leading":51,"trailing":3},"language":"Java","metaVariables":{"single":{},"multi":{"secondary":[{"text":""https://runelite.net/plugin-hub/show/"","range":{"byteOffset":{"start":9678,"end":9717},"start":{"line":310,"column":51},"end":{"line":310,"column":90}}}]},"transformed":{}},"ruleId":"tainted-url-host","severity":"warning","note":"[CWE-20] Improper Input Validation. [REFERENCES]\n - https://cwe.mitre.org/data/definitions/20.html","message":"Avoid building a URL host from untrusted input","labels":[{"text":""https://runelite.net/plugin-hub/show ... [truncated 893 characters] ... mn":44}}},"FILTER":{"text":"query","range":{"byteOffset":{"start":21956,"end":21961},"start":{"line":700,"column":46},"end":{"line":700,"column":51}}},"CTX":{"text":"PluginSearch","range":{"byteOffset":{"start":21927,"end":21939},"start":{"line":700,"column":17},"end":{"line":700,"column":29}}}},"multi":{},"transformed":{}},"ruleId":"ldap-injection-java","severity":"warning","note":"[CWE-90] Improper Neutralization of Special Elements used in an LDAP Query ('LDAP Injection'). Security best practice. [REFERENCES]\n - https://cwe.mitre.org/data/definitions/90.html","message":"Avoid LDAP injections","labels":[{"text":"PluginSearch.search(plugins, query)","range":{"byteOffset":{"start":21927,"end":21962},"start":{"line":700,"column":17},"end":{"line":700,"column":52}},"style":"primary"}]}]
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment Warning |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayRenderer.java (1)
176-193: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winMove the origin menu injection to
MenuOpened.
PostMenuSortis the wrong hook for adding the Shift+click overlay origin submenu; inject it fromonMenuOpenedinstead so it’s built with the rest of the menu entries.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayRenderer.java` around lines 176 - 193, The Shift+click overlay origin submenu is currently being added from `onPostMenuSort`, but it should be injected when the menu is first built. Move the `overlayManager.addOriginMenu(...)` call into `onMenuOpened` in `OverlayRenderer`, and keep the existing guards for `curHoveredOverlay`, `client.isMenuOpen()`, and `client.isKeyPressed(KeyCode.KC_SHIFT)` so the submenu is assembled with the rest of the menu entries.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPlugin.java`:
- Around line 582-600: The submenu action is using the loop index instead of the
encoded submenu identifier, which can break matching later in
MenuEntrySwapperPlugin. Update the submenu click wiring in the block that builds
entries from EntityOps so it passes the encoded sub-ID from the source data (or
make the matcher use the same source) rather than the local subIdx loop counter.
Use the existing symbols EntityOps, packSubID, objectConsumer, and the menu
entry matcher logic to keep both sides aligned.
---
Outside diff comments:
In
`@runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayRenderer.java`:
- Around line 176-193: The Shift+click overlay origin submenu is currently being
added from `onPostMenuSort`, but it should be injected when the menu is first
built. Move the `overlayManager.addOriginMenu(...)` call into `onMenuOpened` in
`OverlayRenderer`, and keep the existing guards for `curHoveredOverlay`,
`client.isMenuOpen()`, and `client.isKeyPressed(KeyCode.KC_SHIFT)` so the
submenu is assembled with the rest of the menu entries.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 8727e976-107a-4586-9fd8-8d5a3d7d13fc
⛔ Files ignored due to path filters (1)
runelite-client/src/main/resources/net/runelite/client/plugins/microbot/shortestpath/blocked_edges.tsvis excluded by!**/*.tsv
📒 Files selected for processing (36)
cache/src/main/java/net/runelite/cache/WorldMapManager.javacache/src/main/java/net/runelite/cache/definitions/loaders/ItemLoader.javacache/src/test/java/net/runelite/cache/WorldMapDumperTest.javagradle.propertiesrunelite-api/src/main/interfaces/interfaces.tomlrunelite-api/src/main/java/net/runelite/api/Client.javarunelite-api/src/main/java/net/runelite/api/EntityOps.javarunelite-api/src/main/java/net/runelite/api/ItemID.javarunelite-api/src/main/java/net/runelite/api/NpcID.javarunelite-api/src/main/java/net/runelite/api/NullItemID.javarunelite-api/src/main/java/net/runelite/api/NullNpcID.javarunelite-api/src/main/java/net/runelite/api/ObjectID.javarunelite-api/src/main/java/net/runelite/api/Perspective.javarunelite-api/src/main/java/net/runelite/api/gameval/DBTableID.javarunelite-api/src/main/java/net/runelite/api/gameval/InterfaceID.javarunelite-api/src/main/java/net/runelite/api/gameval/ItemID.javarunelite-api/src/main/java/net/runelite/api/gameval/NpcID.javarunelite-api/src/main/java/net/runelite/api/gameval/ObjectID1.javarunelite-api/src/main/java/net/runelite/api/gameval/SpriteID.javarunelite-api/src/main/java/net/runelite/api/gameval/VarClientID.javarunelite-api/src/main/java/net/runelite/api/gameval/VarbitID.javarunelite-client/src/main/java/net/runelite/client/game/ItemMapping.javarunelite-client/src/main/java/net/runelite/client/game/npcoverlay/NpcOverlay.javarunelite-client/src/main/java/net/runelite/client/plugins/config/PluginHubPanel.javarunelite-client/src/main/java/net/runelite/client/plugins/gpu/SceneUploader.javarunelite-client/src/main/java/net/runelite/client/plugins/gpu/Zone.javarunelite-client/src/main/java/net/runelite/client/plugins/itemstats/ItemStatChanges.javarunelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPlugin.javarunelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayManager.javarunelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayRenderer.javarunelite-client/src/main/resources/item_variations.jsonrunelite-client/src/main/resources/net/runelite/client/plugins/roofremoval/overrides.jsoncrunelite-client/src/main/scripts/BankMainBuild.hashrunelite-client/src/main/scripts/BankMainBuild.rs2asmrunelite-client/src/main/scripts/PrivateMessage.hashrunelite-client/src/main/scripts/PrivateMessage.rs2asm
💤 Files with no reviewable changes (1)
- runelite-api/src/main/java/net/runelite/api/NullNpcID.java
| EntityOps subops = ops.getSubOps(opIdx); | ||
| if (subops != null) | ||
| { | ||
| String subOp = ops.getSubOp(opIdx, subIdx); | ||
|
|
||
| if (subOp != null) | ||
| int numSubOps = subops.getNumOps(); | ||
| for (int subIdx = 0; subIdx < numSubOps; subIdx++) | ||
| { | ||
| int subID = ops.getSubID(opIdx, subIdx); | ||
| subLeft.createMenuEntry(0) | ||
| .setOption(subOp) | ||
| .setType(MenuAction.RUNELITE) | ||
| .onClick(objectConsumer(composition, subOp, packSubID(opIdx, subID), false)); | ||
| subShift.createMenuEntry(0) | ||
| .setOption(subOp) | ||
| .setType(MenuAction.RUNELITE) | ||
| .onClick(objectConsumer(composition, subOp, packSubID(opIdx, subID), true)); | ||
| String subOp = subops.getOp(subIdx); | ||
|
|
||
| if (subOp != null) | ||
| { | ||
| subLeft.createMenuEntry(0) | ||
| .setOption(subOp) | ||
| .setType(MenuAction.RUNELITE) | ||
| .onClick(objectConsumer(composition, subOp, packSubID(opIdx, subIdx), false)); | ||
| subShift.createMenuEntry(0) | ||
| .setOption(subOp) | ||
| .setType(MenuAction.RUNELITE) | ||
| .onClick(objectConsumer(composition, subOp, packSubID(opIdx, subIdx), true)); | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify whether EntityOps sub-op indexes are guaranteed to match encoded menu-entry sub IDs.
rg -n -C4 'class .*EntityOps|implements EntityOps|getSubOps|getNumOps|getSubID|getSubOp|packSubID|>> 16' --glob '*.java'Repository: chsami/Microbot
Length of output: 153
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate the relevant files and symbols.
git ls-files | rg 'MenuEntrySwapperPlugin\.java|EntityOps|packSubID|getSubOps|getSubID|menuentryswapper'
# Show outline for the plugin file if present.
plugin=$(git ls-files | rg 'MenuEntrySwapperPlugin\.java' | head -n1)
if [ -n "${plugin:-}" ]; then
echo "=== OUTLINE: $plugin ==="
ast-grep outline "$plugin" --view expanded || true
echo "=== PACK/SUB SEARCH ==="
rg -n 'packSubID|getSubOps|getSubID|menuEntry\.getIdentifier\(\) >> 16|>> 16' "$plugin"
fi
# Search repository for EntityOps definitions/usages.
echo "=== ENTITYOPS SEARCH ==="
rg -n 'EntityOps|getSubOps|getSubID|getNumOps|packSubID' --glob '*.java' .Repository: chsami/Microbot
Length of output: 12094
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Search for the classes/methods that define submenu IDs and any matching logic.
fd -a 'EntityOps.java' . || true
fd -a 'MenuEntrySwapperPlugin.java' . || true
rg -n 'class EntityOps|interface EntityOps|record EntityOps|getSubOps|getSubID|getNumOps|packSubID|menuEntry\.getIdentifier\(\) >> 16' --glob '*.java' .Repository: chsami/Microbot
Length of output: 1903
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the plugin code around the changed area and any identifier packing/matching logic.
plugin=$(git ls-files | rg 'MenuEntrySwapperPlugin\.java$' | head -n1)
echo "PLUGIN=$plugin"
if [ -z "$plugin" ]; then
exit 0
fi
echo '--- file outline ---'
ast-grep outline "$plugin" --view expanded || true
echo '--- relevant lines ---'
rg -n -C 3 'packSubID|getSubOps|getSubID|menuEntry\.getIdentifier\(\) >> 16|subIdx|subID' "$plugin"
echo '--- EntityOps search ---'
rg -n -C 3 'class EntityOps|interface EntityOps|record EntityOps|getSubOps|getSubID|getNumOps|packSubID' --glob '*.java' .Repository: chsami/Microbot
Length of output: 13600
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Find the exact plugin file and inspect the changed block plus the matching logic.
plugin=$(git ls-files 'runelite-client/src/main/java/**/MenuEntrySwapperPlugin.java' | head -n1)
printf 'plugin=%s\n' "$plugin"
if [ -n "$plugin" ]; then
nl -ba "$plugin" | sed -n '540,640p'
echo '--- packSubID references ---'
rg -n 'packSubID|menuEntry\.getIdentifier\(\) >> 16|getSubOps|getSubID|subIdx|subID' "$plugin"
fi
echo '--- repo search for EntityOps-like definitions ---'
rg -n 'EntityOps|getSubOps|getSubID|getNumOps|packSubID' runelite-client/src/main/java --glob '*.java'Repository: chsami/Microbot
Length of output: 303
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- EntityOps implementations ---'
rg -n 'implements EntityOps|class .*EntityOps|record .*EntityOps' runelite-api runelite-client cache --glob '*.java'
echo '--- EntityOpsDefinition ---'
sed -n '1,220p' cache/src/main/java/net/runelite/cache/EntityOpsDefinition.java
echo '--- EntityOpsLoader ---'
sed -n '1,220p' cache/src/main/java/net/runelite/cache/definitions/loaders/EntityOpsLoader.javaRepository: chsami/Microbot
Length of output: 7253
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Narrowly inspect the data model for submenu IDs.
sed -n '1,220p' cache/src/main/java/net/runelite/cache/EntityOpsDefinition.java
printf '\n---\n'
sed -n '1,220p' cache/src/main/java/net/runelite/cache/definitions/loaders/EntityOpsLoader.java
printf '\n--- implementations ---\n'
rg -n 'implements EntityOps|class .*EntityOps|record .*EntityOps' runelite-api runelite-client cache --glob '*.java'Repository: chsami/Microbot
Length of output: 7197
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect how submenu entries are stored and exposed.
sed -n '1,220p' cache/src/main/java/net/runelite/cache/EntityOpsDefinition.java
printf '\n---\n'
sed -n '1,220p' cache/src/main/java/net/runelite/cache/definitions/loaders/EntityOpsLoader.java
printf '\n---\n'
rg -n 'implements EntityOps|class .*EntityOps|record .*EntityOps|getSubOps|getNumOps|getOp|subID' runelite-api runelite-client cache --glob '*.java'Repository: chsami/Microbot
Length of output: 44332
Use the encoded submenu ID here. subIdx is only the loop index, but the matcher still compares against menuEntry.getIdentifier() >> 16. Since EntityOps carries a separate subID, this should use that value, or the matcher should switch to the same source.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPlugin.java`
around lines 582 - 600, The submenu action is using the loop index instead of
the encoded submenu identifier, which can break matching later in
MenuEntrySwapperPlugin. Update the submenu click wiring in the block that builds
entries from EntityOps so it passes the encoded sub-ID from the source data (or
make the matcher use the same source) rather than the local subIdx loop counter.
Use the existing symbols EntityOps, packSubID, objectConsumer, and the menu
entry matcher logic to keep both sides aligned.
Problem
The shortest-path A* pathfinder routes through the Al Kharid mine open pit because the cache-derived collision data doesn't mark the terrain as blocking. This causes the walker to get stuck — the path goes green then recalculates to red but the character never moves.
Fix
Add 88 blocked edges around the mine perimeter (x=3281-3300, y=3151-3178) to �locked_edges.tsv. These edge-level blocks prevent the pathfinder from routing through the mine area, forcing it to find an alternate surface route.
Testing
Tested by walking through the Al Kharid area — pathfinder now correctly routes around the mine instead of through it.