Skip to content

Comments

Sailing base#1614

Merged
chsami merged 7 commits intomainfrom
development
Nov 23, 2025
Merged

Sailing base#1614
chsami merged 7 commits intomainfrom
development

Conversation

@chsami
Copy link
Owner

@chsami chsami commented Nov 23, 2025

Microbot – Sailing utilities are live (API preview)

Sailing support in Microbot just got a serious upgrade. We now have a dedicated Rs2Sailing utility that centralises most low-level sailing actions, so scripts can focus on logic instead of boilerplate.

What’s included

  1. Basic boat state and control
  • isOnBoat() – Detects if the player is currently on a player boat.
  • isNavigating() – Checks if the player is at the helm.
  • boardBoat() – Uses gangplanks/moorings to board any nearby sailing vessel.
  • disembarkBoat() – Temporary but robust disembark logic that scans tiles for valid gangplanks/moorings and clicks “Disembark”.
  • navigate() – Moves the player to the steering wheel/helm and enters navigation mode.
  1. Boat movement and sail control
  • Movement state helpers:

    • isMovingForward()
    • isMovingBackward()
    • isStandingStill()
  • Sail control:

    • setSails() – Sets sails to move forward (via the Sailing sidepanel controls).
    • unsetSails() – Brings the boat to a stop.
  • clickSailButton() – Low-level helper to press the set-sail button in the sidepanel.

  1. Heading and pathing towards a target
  • getPlayerBoatLocation() – Resolves the player’s world position relative to the boat, handling instances vs main world projection.

  • getDirection(WorldPoint target) – Computes the 16-direction index towards a target world point.

  • setHeading(Heading heading) – Issues a Set-Heading menu action with the correct world view ID.

  • sailTo(WorldPoint target) – High-level helper that:

    • Boards the boat if needed.
    • Takes the helm.
    • Computes the correct heading towards the target.
    • Sets the heading.
    • Ensures the boat is moving forward.
  1. Automatic sail trimming on gusts of wind
  • handleChatMessage(ChatMessage event) – Listens to game messages.

    • When “You feel a gust of wind” appears, it triggers trimSails() on a separate client thread.
  • trimSails() – Searches for all known sail object IDs (various sizes/woods + colossal sails) and interacts with “Trim”, waiting for XP gain as confirmation.

This gives scripts:

  • Automatic reaction to wind gusts.
  • Centralised trim logic for all supported sails.
  1. Cargo interaction
  • openCargo() – Supports all raft/2x5/large cargo hold variants (all wood types and open/closed IDs) and opens the cargo hold if you are on a boat.
  1. Boat type abstraction (work in progress)
  • getBoatType() – Resolves the boat type based on VarPlayerID.SAILING_SIDEPANEL_BOAT_TYPE.

  • getSteeringForBoatType() – Returns the correct steering object ID per boat type.

    • Currently wired for RAFT, hooks for SKIFF and SLOOP are ready but not filled in yet.
  1. Port task integration
  • getPortTasksVarbits() – Returns all active port task varbits as a Map<PortTaskVarbits, Integer> (only those with value > 0 and type ID).
  • getPortTaskData(int varbitValue) – Maps a varbit value to PortTaskData so scripts can reason about which tasks are active.

How to use this in scripts

Examples of what you can do with this API right now:

  • Sail to specific world points with Rs2Sailing.sailTo(targetWorldPoint).
  • Make scripts that automatically trim sails when wind gusts occur, using handleChatMessage in your plugin.
  • Open and manage cargo holds in a generic way across all boat sizes and wood types.
  • Inspect and react to active port tasks via getPortTasksVarbits() and getPortTaskData(...).

Notes / caveats

  • Disembark handling is explicitly marked as a temporary solution; it will be replaced once the new sailing API is finalised.
  • Boat type handling is structured but only fully implemented for RAFT for now.
  • ignoreBoatWorldView and more advanced world-view logic are available for future expansions and edge cases.

If you are writing sailing-related scripts, you should stop reinventing the wheel and rely on Rs2Sailing for:

  • Boarding/disembarking
  • Navigation mode
  • Heading control
  • Sail control
  • Cargo interaction
  • Port task lookups

Feedback and edge cases (especially odd ports, instances, or disembark failures) are welcome so we can harden this API.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 23, 2025

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This pull request implements a comprehensive sailing system for the microbot plugin. The changes include updating the microbot version in the build configuration, modifying core utility classes to support client-thread execution for safer data access, integrating a new Rs2Sailing utility class with extensive boat navigation and control capabilities, introducing multiple sailing-related data classes (BoatType, Heading, PortLocation, PortPaths, etc.), creating a SailingPanel UI component for route selection and navigation, and refactoring existing utilities like Rs2GameObject and NewMenuEntry to handle world view context. Additionally, the example plugin is updated to demonstrate overlay management using the new infrastructure.

Possibly related PRs

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.45% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title "Sailing base" is vague and does not clearly describe the main change; it lacks specificity about what sailing functionality is being added. Consider a more descriptive title such as "Add Rs2Sailing utility for centralized sailing actions" to better communicate the PR's primary contribution.
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The description is comprehensive and directly related to the changeset, providing clear examples of the new Rs2Sailing API, usage patterns, and implementation details.

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/gameobject/Rs2GameObject.java (2)

1523-1607: Boat-aware scene traversal has inconsistent plane handling and ignores predicates

Two problems in getSceneObjects(...):

  1. Plane/scene selection logic is self-contradictory

    • In the else branch you call player.getWorldView().getScene() even though worldView is known to be null, which can NPE if that path is ever taken.
    • int z = Rs2Sailing.isOnBoat() ? 3 : player.getWorldView().getPlane(); is then used as an upper bound in for (int h = 0; h < z; h++), but z is a plane index, not a plane count. This means:
      • When plane == 0, you never iterate any tiles.
      • When plane == 1, you only scan tiles[0].
      • On boats, if the relevant tiles are not all on planes < 3, they may be skipped.

    This will cause object enumeration to silently miss the actual plane in several configurations.

  2. Boat branch of getSceneObjects(extractor, predicate, anchorLocal, distance) ignores the predicate

    • When Rs2Sailing.isOnBoat() is true, you return getSceneObjects(extractor).collect(...) without applying predicate or distance.
    • Callers like getGameObject(id, ...) rely on this predicate to limit results to a specific id/name, so on a boat they may receive the first arbitrary object in the scene instead of a matching one.

Consider reworking these pieces along the following lines:

  • Use worldView consistently with a safe fallback:
WorldView worldView = player.getWorldView();
WorldView fallback = Microbot.getClient().getTopLevelWorldView();
WorldView effective = worldView != null ? worldView : fallback;
Scene scene = effective.getScene();
Tile[][][] tiles = scene.getTiles();
int z = effective.getPlane(); // or a known constant if you truly need a fixed boat plane
  • Iterate the plane you actually want (e.g. Tile tile = tiles[z][x][y];) or, if you intend to scan all planes, use a fixed bound (for (int h = 0; h < Constants.MAX_Z; h++)), not a single plane index.

  • In getSceneObjects(extractor, predicate, anchorLocal, distance), keep the boat-specific distance relaxation but still honor the predicate:

if (Rs2Sailing.isOnBoat()) {
    return getSceneObjects(extractor)
        .filter(predicate)
        .collect(Collectors.toList());
}

1738-1741: fetchGameObjects(..., WorldPoint) incorrectly delegates to fetchTileObjects

The overload:

private static <T extends TileObject> List<T> fetchGameObjects(Predicate<? super T> predicate, WorldPoint anchor) {
    return fetchTileObjects(predicate, anchor, Constants.SCENE_SIZE);
}

never actually fetches GameObjects; it calls fetchTileObjects, so any use of this method will silently return tile/ground/wall objects instead of game objects.

It should delegate to the fetchGameObjects(predicate, anchor, distance) overload:

 private static <T extends TileObject> List<T> fetchGameObjects(Predicate<? super T> predicate, WorldPoint anchor) {
-    return fetchTileObjects(predicate, anchor, Constants.SCENE_SIZE);
+    return fetchGameObjects(predicate, anchor, Constants.SCENE_SIZE);
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fc3bd32 and 4c0f384.

⛔ Files ignored due to path filters (1)
  • runelite-client/src/main/resources/net/runelite/client/plugins/microbot/shortestpath/sailing_icon.png is excluded by !**/*.png
📒 Files selected for processing (20)
  • runelite-client/pom.xml (1 hunks)
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/Microbot.java (2 hunks)
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/MicrobotPlugin.java (3 hunks)
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExamplePlugin.java (2 hunks)
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExampleScript.java (2 hunks)
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExampleScriptOverlay.java (1 hunks)
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/SailingPanel.java (1 hunks)
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/ShortestPathPlugin.java (3 hunks)
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/gameobject/Rs2GameObject.java (13 hunks)
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/menu/NewMenuEntry.java (4 hunks)
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/player/Rs2Player.java (2 hunks)
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/Rs2Sailing.java (1 hunks)
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/data/BoatPathFollower.java (1 hunks)
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/data/BoatType.java (1 hunks)
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/data/Heading.java (1 hunks)
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/data/LedgerID.java (1 hunks)
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/data/PortLocation.java (1 hunks)
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/data/PortPaths.java (1 hunks)
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/data/PortTaskVarbits.java (1 hunks)
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/data/RelativeMove.java (1 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/**/*.java

📄 CodeRabbit inference engine (AGENTS.md)

runelite-client/src/main/java/net/runelite/client/plugins/microbot/**/*.java: Place all gameplay automation code under runelite-client/src/main/java/net/runelite/client/plugins/microbot
Prefix configuration interfaces with the plugin name (e.g., ExampleConfig) within microbot plugins

Files:

  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/data/RelativeMove.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/data/BoatType.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExampleScriptOverlay.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/SailingPanel.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/player/Rs2Player.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/MicrobotPlugin.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/data/PortTaskVarbits.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/data/PortPaths.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/data/LedgerID.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/data/PortLocation.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/Rs2Sailing.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/data/BoatPathFollower.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExamplePlugin.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExampleScript.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/data/Heading.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/ShortestPathPlugin.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/Microbot.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/gameobject/Rs2GameObject.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/menu/NewMenuEntry.java
runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/**/*.java

📄 CodeRabbit inference engine (AGENTS.md)

Put shared helpers under .../microbot/util

Files:

  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/data/RelativeMove.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/data/BoatType.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/player/Rs2Player.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/data/PortTaskVarbits.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/data/PortPaths.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/data/LedgerID.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/data/PortLocation.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/Rs2Sailing.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/data/BoatPathFollower.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/data/Heading.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/gameobject/Rs2GameObject.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/menu/NewMenuEntry.java
**/*.java

📄 CodeRabbit inference engine (AGENTS.md)

**/*.java: Use tabs for indentation, follow the brace placement style exemplified in MicrobotPlugin.java, and keep lines under 120 characters
Use UpperCamelCase for types and lowerCamelCase for members

Files:

  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/data/RelativeMove.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/data/BoatType.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExampleScriptOverlay.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/SailingPanel.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/player/Rs2Player.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/MicrobotPlugin.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/data/PortTaskVarbits.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/data/PortPaths.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/data/LedgerID.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/data/PortLocation.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/Rs2Sailing.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/data/BoatPathFollower.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExamplePlugin.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExampleScript.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/data/Heading.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/ShortestPathPlugin.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/Microbot.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/gameobject/Rs2GameObject.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/menu/NewMenuEntry.java
**/pom.xml

📄 CodeRabbit inference engine (AGENTS.md)

Target Java 11 via maven-compiler-plugin using 11 in all module POMs

Files:

  • runelite-client/pom.xml
runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/**/*.java

📄 CodeRabbit inference engine (AGENTS.md)

Keep runnable examples under .../microbot/example

Files:

  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExampleScriptOverlay.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExamplePlugin.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExampleScript.java
runelite-client/src/main/java/net/runelite/client/plugins/microbot/@(!(util|example))/**/*.java

📄 CodeRabbit inference engine (AGENTS.md)

For new automation scripts, reuse the scheduler pattern demonstrated in ExampleScript

Files:

  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/SailingPanel.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/ShortestPathPlugin.java
runelite-client/src/main/java/net/runelite/client/plugins/microbot/MicrobotPlugin.java

📄 CodeRabbit inference engine (AGENTS.md)

When adding panel controls or overlays, update the Microbot navigation panel setup in MicrobotPlugin and provide default config values

Files:

  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/MicrobotPlugin.java
🧠 Learnings (12)
📓 Common learnings
Learnt from: CR
Repo: chsami/Microbot PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-10-16T17:14:29.778Z
Learning: Applies to runelite-client/src/main/java/net/runelite/client/plugins/microbot/MicrobotPlugin.java : When adding panel controls or overlays, update the Microbot navigation panel setup in MicrobotPlugin and provide default config values
📚 Learning: 2025-10-16T17:14:29.778Z
Learnt from: CR
Repo: chsami/Microbot PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-10-16T17:14:29.778Z
Learning: Applies to runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/**/*.java : Put shared helpers under .../microbot/util

Applied to files:

  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/data/RelativeMove.java
  • runelite-client/pom.xml
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/SailingPanel.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/player/Rs2Player.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/MicrobotPlugin.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/Rs2Sailing.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExamplePlugin.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExampleScript.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/ShortestPathPlugin.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/Microbot.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/gameobject/Rs2GameObject.java
📚 Learning: 2025-10-16T17:14:29.778Z
Learnt from: CR
Repo: chsami/Microbot PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-10-16T17:14:29.778Z
Learning: Applies to runelite-client/src/main/java/net/runelite/client/plugins/microbot/**/*.java : Place all gameplay automation code under runelite-client/src/main/java/net/runelite/client/plugins/microbot

Applied to files:

  • runelite-client/pom.xml
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExampleScriptOverlay.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/SailingPanel.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/player/Rs2Player.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/MicrobotPlugin.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/Rs2Sailing.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/data/BoatPathFollower.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExamplePlugin.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExampleScript.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/ShortestPathPlugin.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/Microbot.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/gameobject/Rs2GameObject.java
📚 Learning: 2025-10-16T17:14:29.778Z
Learnt from: CR
Repo: chsami/Microbot PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-10-16T17:14:29.778Z
Learning: Applies to runelite-client/src/main/java/net/runelite/client/plugins/microbot/**/*.java : Prefix configuration interfaces with the plugin name (e.g., ExampleConfig) within microbot plugins

Applied to files:

  • runelite-client/pom.xml
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExampleScriptOverlay.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/player/Rs2Player.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/MicrobotPlugin.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExamplePlugin.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExampleScript.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/ShortestPathPlugin.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/Microbot.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/gameobject/Rs2GameObject.java
📚 Learning: 2025-10-16T17:14:29.778Z
Learnt from: CR
Repo: chsami/Microbot PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-10-16T17:14:29.778Z
Learning: Applies to runelite-client/src/main/java/net/runelite/client/plugins/microbot/@(!(util|example))/**/*.java : For new automation scripts, reuse the scheduler pattern demonstrated in ExampleScript

Applied to files:

  • runelite-client/pom.xml
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExampleScriptOverlay.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/SailingPanel.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/player/Rs2Player.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/MicrobotPlugin.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExamplePlugin.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExampleScript.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/ShortestPathPlugin.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/Microbot.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/gameobject/Rs2GameObject.java
📚 Learning: 2025-10-16T17:14:29.778Z
Learnt from: CR
Repo: chsami/Microbot PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-10-16T17:14:29.778Z
Learning: Applies to runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/**/*.java : Keep runnable examples under .../microbot/example

Applied to files:

  • runelite-client/pom.xml
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExampleScriptOverlay.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/player/Rs2Player.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/MicrobotPlugin.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExamplePlugin.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExampleScript.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/ShortestPathPlugin.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/Microbot.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/gameobject/Rs2GameObject.java
📚 Learning: 2025-10-16T17:14:29.778Z
Learnt from: CR
Repo: chsami/Microbot PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-10-16T17:14:29.778Z
Learning: Applies to runelite-client/src/main/java/net/runelite/client/plugins/microbot/MicrobotPlugin.java : When adding panel controls or overlays, update the Microbot navigation panel setup in MicrobotPlugin and provide default config values

Applied to files:

  • runelite-client/pom.xml
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExampleScriptOverlay.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/SailingPanel.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/player/Rs2Player.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/MicrobotPlugin.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/Rs2Sailing.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExamplePlugin.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExampleScript.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/ShortestPathPlugin.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/Microbot.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/gameobject/Rs2GameObject.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/menu/NewMenuEntry.java
📚 Learning: 2025-10-16T17:14:29.778Z
Learnt from: CR
Repo: chsami/Microbot PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-10-16T17:14:29.778Z
Learning: Applies to runelite-client/target/*.jar : Distributable JARs should be produced in runelite-client/target/

Applied to files:

  • runelite-client/pom.xml
📚 Learning: 2025-10-16T17:14:29.778Z
Learnt from: CR
Repo: chsami/Microbot PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-10-16T17:14:29.778Z
Learning: Applies to runelite-client/src/test/java/**/*.java : Mirror production package structure for tests under runelite-client/src/test/java

Applied to files:

  • runelite-client/pom.xml
📚 Learning: 2025-08-31T19:19:07.691Z
Learnt from: chsami
Repo: chsami/Microbot PR: 1455
File: runelite-client/src/main/java/net/runelite/client/plugins/microbot/MicrobotVersionChecker.java:112-132
Timestamp: 2025-08-31T19:19:07.691Z
Learning: The Microbot project uses only numeric versioning (e.g., "1.2.3") without any alphanumeric suffixes like "-beta" or "-alpha", so simple integer parsing of version segments is safe.

Applied to files:

  • runelite-client/pom.xml
📚 Learning: 2025-08-25T15:51:39.272Z
Learnt from: runsonmypc
Repo: chsami/Microbot PR: 1417
File: runelite-client/src/main/java/net/runelite/client/plugins/microbot/agility/AgilityScript.java:13-13
Timestamp: 2025-08-25T15:51:39.272Z
Learning: Both net.runelite.api.ItemID and net.runelite.api.gameval.ItemID are valid import paths in the RuneLite codebase. The microbot plugins consistently use net.runelite.api.ItemID as their established pattern.

Applied to files:

  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/MicrobotPlugin.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/data/LedgerID.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/Microbot.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/gameobject/Rs2GameObject.java
📚 Learning: 2025-08-23T16:17:37.615Z
Learnt from: g-mason0
Repo: chsami/Microbot PR: 1423
File: runelite-client/src/main/java/net/runelite/client/plugins/microbot/externalplugins/MicrobotPluginManager.java:366-389
Timestamp: 2025-08-23T16:17:37.615Z
Learning: The ScheduledExecutorService injected into MicrobotPluginManager (and other RuneLite classes) is configured as a single-threaded executor via Executors.newSingleThreadScheduledExecutor() at RuneLiteModule.java line 130, wrapped in ExecutorServiceExceptionLogger. This ensures all async tasks execute sequentially, preventing race conditions.

Applied to files:

  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExampleScript.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/Microbot.java
🧬 Code graph analysis (6)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/SailingPanel.java (1)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/data/BoatPathFollower.java (1)
  • BoatPathFollower (10-72)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/data/LedgerID.java (1)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/gameobject/ObjectID.java (1)
  • ObjectID (10-33)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/data/PortLocation.java (1)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/gameobject/ObjectID.java (1)
  • ObjectID (10-33)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/Rs2Sailing.java (3)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/gameobject/Rs2GameObject.java (1)
  • Rs2GameObject (38-2064)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/menu/NewMenuEntry.java (1)
  • NewMenuEntry (11-330)
runelite-api/src/main/java/net/runelite/api/gameval/InterfaceID.java (1)
  • SailingSidepanel (30596-30638)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExampleScript.java (1)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/data/BoatPathFollower.java (1)
  • BoatPathFollower (10-72)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/ShortestPathPlugin.java (1)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/PohPanel.java (1)
  • PohPanel (30-304)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build
  • GitHub Check: Build

Comment on lines 948 to 957
public static WorldPoint getWorldLocation() {
if (Microbot.getClient().getTopLevelWorldView().getScene().isInstance()) {
LocalPoint l = LocalPoint.fromWorld(Microbot.getClient().getTopLevelWorldView(), Microbot.getClient().getLocalPlayer().getWorldLocation());
return WorldPoint.fromLocalInstance(Microbot.getClient(), l);
} else {
if (Microbot.getClient().getLocalPlayer() == null) {
return null; // Handle case where local player is not available
return Microbot.getClientThread().invoke(() -> {
if (Microbot.getClient().getTopLevelWorldView().getScene().isInstance()) {
LocalPoint l = LocalPoint.fromWorld(Microbot.getClient().getTopLevelWorldView(), Microbot.getClient().getLocalPlayer().getWorldLocation());
return WorldPoint.fromLocalInstance(Microbot.getClient(), l);
} else {
return Microbot.getClient().getLocalPlayer().getWorldLocation();
}
return Microbot.getClient().getLocalPlayer().getWorldLocation();
}
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

getWorldLocation now assumes non-null localPlayer, breaking callers that expect null and risking NPEs

The new getWorldLocation() body dereferences Microbot.getClient().getLocalPlayer() without a null check and always returns a value from invoke. Several callers in this class explicitly handle a null world location (e.g., isInArea, isPlayerAreaIntersecting), but with this change they will instead hit a NullPointerException during logout / world-hop / load screens rather than getting null. That’s inconsistent with how the method is used elsewhere.

Consider guarding for a missing local player and aligning with the runOnClientThreadOptional pattern already used in this class:

-    public static WorldPoint getWorldLocation() {
-        return Microbot.getClientThread().invoke(() -> {
-            if (Microbot.getClient().getTopLevelWorldView().getScene().isInstance()) {
-                LocalPoint l = LocalPoint.fromWorld(Microbot.getClient().getTopLevelWorldView(), Microbot.getClient().getLocalPlayer().getWorldLocation());
-                return WorldPoint.fromLocalInstance(Microbot.getClient(), l);
-            } else {
-                return Microbot.getClient().getLocalPlayer().getWorldLocation();
-            }
-        });
-    }
+    public static WorldPoint getWorldLocation() {
+        return Microbot.getClientThread().runOnClientThreadOptional(() -> {
+            if (Microbot.getClient().getLocalPlayer() == null)
+            {
+                return null;
+            }
+
+            if (Microbot.getClient().getTopLevelWorldView().getScene().isInstance())
+            {
+                LocalPoint l = LocalPoint.fromWorld(
+                    Microbot.getClient().getTopLevelWorldView(),
+                    Microbot.getClient().getLocalPlayer().getWorldLocation());
+                return l != null ? WorldPoint.fromLocalInstance(Microbot.getClient(), l) : null;
+            }
+
+            return Microbot.getClient().getLocalPlayer().getWorldLocation();
+        }).orElse(null);
+    }

This preserves client-thread safety, avoids NPEs when the player is unavailable, and keeps the “null when unknown” contract that the rest of the class relies on.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public static WorldPoint getWorldLocation() {
if (Microbot.getClient().getTopLevelWorldView().getScene().isInstance()) {
LocalPoint l = LocalPoint.fromWorld(Microbot.getClient().getTopLevelWorldView(), Microbot.getClient().getLocalPlayer().getWorldLocation());
return WorldPoint.fromLocalInstance(Microbot.getClient(), l);
} else {
if (Microbot.getClient().getLocalPlayer() == null) {
return null; // Handle case where local player is not available
return Microbot.getClientThread().invoke(() -> {
if (Microbot.getClient().getTopLevelWorldView().getScene().isInstance()) {
LocalPoint l = LocalPoint.fromWorld(Microbot.getClient().getTopLevelWorldView(), Microbot.getClient().getLocalPlayer().getWorldLocation());
return WorldPoint.fromLocalInstance(Microbot.getClient(), l);
} else {
return Microbot.getClient().getLocalPlayer().getWorldLocation();
}
return Microbot.getClient().getLocalPlayer().getWorldLocation();
}
});
public static WorldPoint getWorldLocation() {
return Microbot.getClientThread().runOnClientThreadOptional(() -> {
if (Microbot.getClient().getLocalPlayer() == null)
{
return null;
}
if (Microbot.getClient().getTopLevelWorldView().getScene().isInstance())
{
LocalPoint l = LocalPoint.fromWorld(
Microbot.getClient().getTopLevelWorldView(),
Microbot.getClient().getLocalPlayer().getWorldLocation());
return l != null ? WorldPoint.fromLocalInstance(Microbot.getClient(), l) : null;
}
return Microbot.getClient().getLocalPlayer().getWorldLocation();
}).orElse(null);
}
🤖 Prompt for AI Agents
In
runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/player/Rs2Player.java
around lines 949-957, the new getWorldLocation implementation dereferences
Microbot.getClient().getLocalPlayer() on the client thread and always returns a
value, causing NPEs when the local player is null; change it to follow the
runOnClientThreadOptional pattern: run the logic on the client thread, check if
getLocalPlayer() is null and if so return null, otherwise compute
LocalPoint.fromWorld(...) and WorldPoint.fromLocalInstance(...) and return that;
ensure the method preserves client-thread safety and the original contract of
returning null when the player/world location is unavailable.

Comment on lines +52 to +79
private static final Set<Integer> GANGPLANK_IDS;
private static final Set<Integer> NOTICEBOARD_IDS;


PortLocation(String name, Integer sailingLevelRequired, int gangplankObject, int noticeboardObject, WorldPoint navigationLocation)
{
this.name = name;
this.sailingLevelRequired = sailingLevelRequired;
this.gangplankObject = gangplankObject;
this.noticeboardObject = noticeboardObject;
this.navigationLocation = navigationLocation;
}

static
{
Set<Integer> gangplanks = new HashSet<>();
Set<Integer> noticeboards = new HashSet<>();
for (PortLocation p : values())
{
gangplanks.add(p.gangplankObject);
if (p.noticeboardObject != -1)
{
noticeboards.add(p.noticeboardObject);
}
}
GANGPLANK_IDS = Collections.unmodifiableSet(gangplanks);
NOTICEBOARD_IDS = Collections.unmodifiableSet(noticeboards);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Sentinel gangplank id -1 is treated as a real gangplank in GANGPLANK_IDS

EMPTY is defined with gangplankObject = -1, but the static init unconditionally adds every gangplankObject to GANGPLANK_IDS. As a result, isGangplank(-1) will return true, while isNoticeboard(-1) correctly returns false because -1 is filtered out there. That’s inconsistent sentinel handling and could misclassify invalid IDs.

You can mirror the noticeboard logic for gangplanks:

-        for (PortLocation p : values())
-        {
-            gangplanks.add(p.gangplankObject);
-            if (p.noticeboardObject != -1)
-            {
-                noticeboards.add(p.noticeboardObject);
-            }
-        }
+        for (PortLocation p : values())
+        {
+            if (p.gangplankObject != -1)
+            {
+                gangplanks.add(p.gangplankObject);
+            }
+            if (p.noticeboardObject != -1)
+            {
+                noticeboards.add(p.noticeboardObject);
+            }
+        }

This keeps both helpers aligned and avoids ever treating the EMPTY sentinel as a real in-game object.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private static final Set<Integer> GANGPLANK_IDS;
private static final Set<Integer> NOTICEBOARD_IDS;
PortLocation(String name, Integer sailingLevelRequired, int gangplankObject, int noticeboardObject, WorldPoint navigationLocation)
{
this.name = name;
this.sailingLevelRequired = sailingLevelRequired;
this.gangplankObject = gangplankObject;
this.noticeboardObject = noticeboardObject;
this.navigationLocation = navigationLocation;
}
static
{
Set<Integer> gangplanks = new HashSet<>();
Set<Integer> noticeboards = new HashSet<>();
for (PortLocation p : values())
{
gangplanks.add(p.gangplankObject);
if (p.noticeboardObject != -1)
{
noticeboards.add(p.noticeboardObject);
}
}
GANGPLANK_IDS = Collections.unmodifiableSet(gangplanks);
NOTICEBOARD_IDS = Collections.unmodifiableSet(noticeboards);
}
private static final Set<Integer> GANGPLANK_IDS;
private static final Set<Integer> NOTICEBOARD_IDS;
PortLocation(String name, Integer sailingLevelRequired, int gangplankObject, int noticeboardObject, WorldPoint navigationLocation)
{
this.name = name;
this.sailingLevelRequired = sailingLevelRequired;
this.gangplankObject = gangplankObject;
this.noticeboardObject = noticeboardObject;
this.navigationLocation = navigationLocation;
}
static
{
Set<Integer> gangplanks = new HashSet<>();
Set<Integer> noticeboards = new HashSet<>();
for (PortLocation p : values())
{
if (p.gangplankObject != -1)
{
gangplanks.add(p.gangplankObject);
}
if (p.noticeboardObject != -1)
{
noticeboards.add(p.noticeboardObject);
}
}
GANGPLANK_IDS = Collections.unmodifiableSet(gangplanks);
NOTICEBOARD_IDS = Collections.unmodifiableSet(noticeboards);
}
🤖 Prompt for AI Agents
In
runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/data/PortLocation.java
around lines 52 to 79, the static initializer unconditionally adds every
gangplankObject (including the sentinel -1 for EMPTY) to GANGPLANK_IDS which
misclassifies the sentinel as a real object; change the initializer to only add
gangplankObject values when they are not -1 (mirror the existing noticeboard
filtering) so GANGPLANK_IDS never contains the sentinel value.

Comment on lines +2113 to +2123
VOID_KNIGHTS_OUTPOST_PORT_TYRAS(
PortLocation.VOID_KNIGHTS_OUTPOST,
PortLocation.PORT_KHAZARD,
new RelativeMove(-309, 0),
new RelativeMove(-12, 12),
new RelativeMove(-114, 0),
new RelativeMove(-151, 151),
new RelativeMove(0, 134),
new RelativeMove(63, 63),
new RelativeMove(0, 49),
new RelativeMove(13, 13)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Enum VOID_KNIGHTS_OUTPOST_PORT_TYRAS ends at the wrong port

VOID_KNIGHTS_OUTPOST_PORT_TYRAS is declared with PortLocation.PORT_KHAZARD as its end, even though:

  • Its name says ...PORT_TYRAS.
  • There is already a VOID_KNIGHTS_OUTPOST_PORT_KHAZARD route.

This duplicates the VOID→PORT_KHAZARD path and leaves no VOID→PORT_TYRAS path for lookups based on start/end. Suggested fix:

-    VOID_KNIGHTS_OUTPOST_PORT_TYRAS(
-            PortLocation.VOID_KNIGHTS_OUTPOST,
-            PortLocation.PORT_KHAZARD,
+    VOID_KNIGHTS_OUTPOST_PORT_TYRAS(
+            PortLocation.VOID_KNIGHTS_OUTPOST,
+            PortLocation.PORT_TYRAS,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
VOID_KNIGHTS_OUTPOST_PORT_TYRAS(
PortLocation.VOID_KNIGHTS_OUTPOST,
PortLocation.PORT_KHAZARD,
new RelativeMove(-309, 0),
new RelativeMove(-12, 12),
new RelativeMove(-114, 0),
new RelativeMove(-151, 151),
new RelativeMove(0, 134),
new RelativeMove(63, 63),
new RelativeMove(0, 49),
new RelativeMove(13, 13)
VOID_KNIGHTS_OUTPOST_PORT_TYRAS(
PortLocation.VOID_KNIGHTS_OUTPOST,
PortLocation.PORT_TYRAS,
new RelativeMove(-309, 0),
new RelativeMove(-12, 12),
new RelativeMove(-114, 0),
new RelativeMove(-151, 151),
new RelativeMove(0, 134),
new RelativeMove(63, 63),
new RelativeMove(0, 49),
new RelativeMove(13, 13)
🤖 Prompt for AI Agents
In
runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/sailing/data/PortPaths.java
around lines 2113 to 2123, the enum entry VOID_KNIGHTS_OUTPOST_PORT_TYRAS
incorrectly uses PortLocation.PORT_KHAZARD as its end which duplicates the
existing VOID_KNIGHTS_OUTPOST_PORT_KHAZARD route; change the second constructor
argument to PortLocation.PORT_TYRAS so the enum reflects VOID → PORT_TYRAS, and
verify there are no other duplicate entries or mismatched start/end pairs for
this route.

@chsami chsami merged commit 195dd54 into main Nov 23, 2025
3 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Dec 2, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant