Skip to content

fix(PVirewatchKiller): skip loop and overlay until fight area is set (1.0.5) - #645

Merged
chsami merged 3 commits into
developmentfrom
claude/err-virewatch-null-fight-area-before-first-tick
Oct 16, 2026
Merged

chsami merged 3 commits into
developmentfrom
claude/err-virewatch-null-fight-area-before-first-tick

Conversation

@chsami

@chsami chsami commented Oct 16, 2026

Copy link
Copy Markdown
Owner

Problem

Client error telemetry group 2413 (PVirewatchKiller 1.0.4, Microbot 2.6.32): 3 occurrences across 1 report, all on 2026-10-16.

java.lang.NullPointerException: Cannot invoke "net.runelite.api.coords.WorldArea.contains(net.runelite.api.co...
    at net.runelite.client.plugins.microbot.virewatch.PVirewatchScript.lambda$run$3:22
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run

Root cause

PVirewatchKillerPlugin.fightArea starts out null. It is only assigned in onGameTick once startingLocation has been captured, or in onConfigChanged. shutDown() sets it back to null. The script loop runs on its own scheduler and called plugin.fightArea.contains(...) without a null check. That throws before the first GameTick after start, and again after shutdown clears the field. The overlay's NPC outline block had the same unguarded dereference, so it hit an NPE on every frame between startUp() adding the overlay and the first tick.

Change

  • PVirewatchScript: each iteration reads the local player location, fightArea and startingLocation once, and returns early while any of them is null (the hasFightState helper). The rest of the iteration uses those locals.
  • PVirewatchScript: the "away from start" check used != on WorldPoint, which compares identity and so was always true. It now uses equals() (the isAwayFromStart helper). This is a small behaviour change: walkTo is now skipped when the player is already on the start tile. Before, it was called with distance 0, which did nothing.
  • PVirewatchKillerOverlay.render: reads plugin.fightArea into one local per frame. Both the fight-area tile loop and the NPC outline block use that local and run only when it is non-null. A shutDown() mid-frame can therefore no longer cause an NPE.
  • Plugin version bumped to 1.0.5.

Validation

  • ./gradlew build -PpluginList=PVirewatchKillerPlugin test --tests '*PVirewatchScriptTest*': BUILD SUCCESSFUL
  • PVirewatchScriptTest: 5 tests, 0 failures. Covers the null fight area or start location before the first tick, the missing local player, and the equals-based start tile comparison.

Known gaps

  • The tests cover only the static helpers. The script loop and render() depend on Microbot statics and the NPC cache, so they are not unit-tested and have not been run against a live client.
  • onGameTick still calls getLocalPlayer() without a null check. No telemetry reports point there, so it is out of scope.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LjtEMzQHGuq4M1Mpqk9pPV

chsami and others added 3 commits October 16, 2026 17:55
The script loop starts with no initial delay and dereferenced
plugin.fightArea before the first GameTick assigned it, and again after
the plugin stop hook cleared it. Read the player location once with a
null guard, snapshot fightArea and startingLocation, and return early
while any of them is missing. Compare WorldPoints with equals instead of
identity so the return-to-start branch is no longer always taken.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LjtEMzQHGuq4M1Mpqk9pPV
@chsami
chsami merged commit 01117fe into development Oct 16, 2026
1 check passed
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