Conversation
Implemented detection and handling for the Struggling Sapling forestry event in AutoWoodcuttingScript. Updated overlay to display current forestry event. Extended items to bank in config. Improved event detection and handling logic for forestry events, and refactored related code for clarity and maintainability.
Implemented detection and handling for the Struggling Sapling forestry event in AutoWoodcuttingScript. Updated overlay to display current forestry event. Extended items to bank in config. Improved event detection and handling logic for forestry events, and refactored related code for clarity and maintainability.
…stry- # Conflicts: # runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/AutoWoodcuttingScript.java
test and fix events: - all events work somewhat - for some events the interaction speed is abysmal, but that can't easily be changed without rewriting the core plugin loop - fix burning logs on existing campfires (not getting stuck in last animation anymore)
…g changes without restart
…oodcutting-and-Forestry- # Conflicts: # runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/AutoWoodcuttingScript.java # runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/enums/WoodcuttingTree.java
…oodcutting-and-Forestry-
…oodcutting-and-Forestry-
…ing-and-Forestry-
…oodcutting-and-Forestry-
- improve some events - add tracking of wrong ingredients for sapling
…instead of trying to re-load all sideloaded plugins
Adjust Install workflow of MicrobotPluginManager
…thods, marking them as deprecated
|
Caution Review failedThe pull request is closed. WalkthroughBumps microbot.version and implements extensive plugin-management, forestry/woodcutting features, GUI updates, utilities tweaks, and many new forestry event classes. Changes include manifest caching/refresh, sideload install/load/sync, a state-machine woodcutting script, UI/overlay updates, reflection-to-API refactors, and a new bank helper. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant Manager as MicrobotPluginManager
participant Client as MicrobotPluginClient
participant FS as FileSystem
participant Bus as EventBus
Note over Manager: Initialization
User->>Manager: instantiate
Manager->>Client: download manifests
Client-->>Manager: manifests
Manager->>Manager: build manifestMap, compare (keys+SHA256)
alt manifests changed
Manager->>Bus: post ExternalPluginsChanged
end
Manager->>Manager: schedule periodic refresh (10m)
Note over Manager,FS: Install path
User->>Manager: install(manifest)
Manager->>Client: fetch jar URL & bytes
Client-->>Manager: jar bytes
Manager->>Manager: verify SHA-256
Manager->>FS: write jar, update installed list
Manager->>Manager: loadSideLoadPlugin(internalName)
Manager->>Bus: post ExternalPluginsChanged
sequenceDiagram
autonumber
participant Plugin as AutoWoodcuttingPlugin
participant Script as AutoWoodcuttingScript
participant Events as ForestryEvents*
participant Overlay as AutoWoodcuttingOverlay
participant Config
Note over Plugin: Startup
Plugin->>Plugin: addEvents() if config.enableForestry
Plugin->>Script: start main loop
loop main
Script->>Script: preFlightChecks
alt WOODCUTTING
Script->>Script: handleWoodcutting / handleLooting
else FIREMAKING
Script->>Script: handleFiremaking
else RESETTING
Script->>Script: handleBanking/resetInventory
end
Script->>Overlay: update stats (xp, logs, time)
alt forestry triggered
Plugin->>Events: execute selected blocking event
Events-->>Plugin: update currentForestryEvent
end
end
sequenceDiagram
autonumber
participant Panel as MicrobotPluginHubPanel
participant Manager as MicrobotPluginManager
participant Counts as Plugin Count Service
participant UI
Panel->>Manager: getManifestMap().values()
Panel->>Counts: fetch pluginCounts
alt counts OK
Panel->>UI: build list from manifests + counts
else counts error
Panel->>UI: show Retry button
end
Manager-->>Panel: ExternalPluginsChanged event
Panel->>Panel: reload using cached manifests
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. 📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 💡 Knowledge Base configuration:
You can enable these sources in your CodeRabbit configuration. 📒 Files selected for processing (20)
✨ Finishing Touches
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
|
@CodeRabbit summary |
✅ Actions performedSummary regeneration triggered. |
There was a problem hiding this comment.
Actionable comments posted: 11
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
runelite-client/pom.xml (1)
472-481: Stray characters in shade plugin block will break the POM.There’s a literal "§y" appended after the closing DSA exclude on Line 477. This will cause XML parsing errors during build.
Apply this diff to remove the stray characters:
- <exclude>META-INF/*.DSA</exclude>§y + <exclude>META-INF/*.DSA</exclude>runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/AutoWoodcuttingConfig.java (1)
133-141: User-facing typo: “Comma seperated” → “Comma separated”.Visible in the configuration UI; fix spelling.
- name = "Items to bank (Comma seperated)", + name = "Items to bank (Comma separated)",
🧹 Nitpick comments (34)
runelite-client/pom.xml (1)
49-53: Consider using HTTPS for distributionManagement snapshotRepository.The snapshot repo URL is plain HTTP. If your Nexus/Artifactory supports TLS, switching to HTTPS avoids MITM risks during deploys.
- <url>http://138.201.81.246:8081/repository/microbot-snapshot/</url> + <url>https://138.201.81.246:8081/repository/microbot-snapshot/</url>runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/reflection/Rs2Reflection.java (2)
106-118: Javadoc says “by using reflection” but method is a direct accessor; adjust docs and consider removing @SneakyThrows.Method body is fine and the deprecation makes sense. Update the summary to reflect direct API usage, and drop @SneakyThrows since nothing here throws.
- /** - * Gets the animation of an NPC by using reflection. - * @param npc - * @return - */ + /** + * Returns the current animation id of the given NPC via the public API. + * Returns -1 when npc is null. + */ @@ - @SneakyThrows @Deprecated(since="1.9.8.7 - Runelite exposes all animations", forRemoval=true) public static int getAnimation(NPC npc) { if (npc == null) { return -1; } return npc.getAnimation(); }
120-148: Bound-check overheadSpriteId to avoid IndexOutOfBounds; prefer direct API where available.Mapping sprite ids to HeadIcon via ordinal is brittle; unexpected sprite ids can throw. Add a bounds check and consider a future replacement with NPC.getOverheadIcon().
- @SneakyThrows - @Deprecated(since="1.9.8.7 - Runelite exposes overheads on npcs", forRemoval = true) + @Deprecated(since="1.9.8.7 - Runelite exposes overheads on npcs", forRemoval = true) public static HeadIcon getHeadIcon(Rs2NpcModel npc) { if (npc == null) { return null; } if (npc.getOverheadSpriteIds() == null) { Microbot.log("Failed to find the correct overhead prayer."); return null; } for (int i = 0; i < npc.getOverheadSpriteIds().length; i++) { int overheadSpriteId = npc.getOverheadSpriteIds()[i]; if (overheadSpriteId == -1) continue; - return HeadIcon.values()[overheadSpriteId]; + HeadIcon[] icons = HeadIcon.values(); + if (overheadSpriteId >= 0 && overheadSpriteId < icons.length) { + return icons[overheadSpriteId]; + } else { + Microbot.log("Unexpected overheadSpriteId: " + overheadSpriteId); + } } Microbot.log("Found overheadSpriteIds: " + Arrays.toString(npc.getOverheadSpriteIds()) + " but failed to find valid overhead prayer."); return null; }Optionally, introduce a non-deprecated overload:
public static HeadIcon getHeadIcon(NPC npc) { return npc == null ? null : npc.getOverheadIcon(); }runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/Forestry/FoxEvent.java (2)
38-40: Wait for trap state change to avoid re-click spamAfter invoking Disarm, immediately looping can spam interactions. Wait until the trap despawns/changes before retrying.
Rs2Npc.interact(trap, "Disarm"); - Rs2Player.waitForAnimation(1000); + Rs2Player.waitForAnimation(1000); + // Wait up to 4s for the trap to disappear or become non-interactable + net.runelite.client.plugins.microbot.util.Global.sleepUntil( + () -> net.runelite.client.plugins.microbot.util.npc.Rs2Npc.getNpc( + net.runelite.api.gameval.NpcID.GATHERING_EVENT_POACHERS_TRAP) == null, 4000);
41-43: Optionally reset forestry state on exitOther flows may expect currentForestryEvent to clear once handling is done.
- Microbot.log("FoxEvent: Finished executing the Fox event.", Level.INFO); - return true; + Microbot.log("FoxEvent: Finished executing the Fox event.", Level.INFO); + plugin.currentForestryEvent = net.runelite.client.plugins.microbot.woodcutting.enums.ForestryEvents.NONE; + return true;runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/Forestry/LeprechaunEvent.java (2)
43-47: Use distance-based arrival check and a more forgiving timeoutExact WorldPoint equality can be brittle (e.g., pathing to adjacent tile). Prefer a distance check and give walking a bit more time.
- if (!Rs2Player.getWorldLocation().equals(location)) { + if (net.runelite.client.plugins.microbot.util.player.Rs2Player.distanceTo(location) > 0) { Microbot.log("LeprechaunEvent: Walking to the end of the rainbow at " + location, Level.INFO); Rs2Walker.walkFastCanvas(location); - Global.sleepUntil(() -> Rs2Player.getWorldLocation().equals(location), 5000); + Global.sleepUntil( + () -> net.runelite.client.plugins.microbot.util.player.Rs2Player.distanceTo(location) == 0 + || !this.validate(), + 10000 + ); }
49-52: Relocate TODO above the return (unreachable comment)The TODO appears after
return true;, making it effectively unreachable context. Move it above the return or to the class-level Javadoc.runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/Forestry/EggEvent.java (3)
74-77: Add backoff when no nests or pheasants are presentThis
continuecan tight-loop. Sleep briefly to reduce CPU churn.- if (nests.isEmpty() || pheasants.isEmpty()) { + if (nests.isEmpty() || pheasants.isEmpty()) { Microbot.log("EggEvent: No pheasant nests found, cannot proceed with egg event."); - continue; + net.runelite.client.plugins.microbot.util.Global.sleep(200); + continue; }
86-96: Guard null closestNest before interacting and add small delay
closestNestcan be null ifemptyNestsis empty, leading to repeated failed interacts and a hot loop.- var closestNest = emptyNests.stream() + var closestNest = emptyNests.stream() .min(Comparator.comparingInt(o -> o.getWorldLocation().distanceTo(Rs2Player.getWorldLocation()))) .orElse(null); - var interact = Rs2GameObject.interact(closestNest); - if (!interact) { - Microbot.log("EggEvent: Failed to interact with the pheasant nest."); - Microbot.log("EggEvent: Closest nest is null? " + (closestNest == null)); - } + if (closestNest == null) { + Microbot.log("EggEvent: No reachable empty nests found; waiting..."); + net.runelite.client.plugins.microbot.util.Global.sleep(300); + continue; + } + + boolean interact = Rs2GameObject.interact(closestNest); + if (!interact) { + Microbot.log("EggEvent: Failed to interact with the pheasant nest at " + closestNest.getWorldLocation()); + } Rs2Player.waitForAnimation();
48-57: Inventory full path: confirm fallback when no logs are presentIf the inventory is full and there’s no tree log to drop, the event cannot progress. Consider an alternate drop strategy (e.g., least valuable log), or abort with a clear log message so the script can recover.
I can propose a safe fallback (e.g., drop a single low-value item from a configurable allowlist) if you want.
runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/Forestry/RootEvent.java (2)
58-64: Interacting-with-same-root detection is unreliableThe “already interacting” check uses actor interaction and compares an actor’s location with an object’s location; for object chopping this generally won’t match. If the intent is to avoid re-clicking while already chopping, a simpler and reliable check is to skip when the player is currently animating.
- if (Rs2Player.isInteracting() && Rs2Player.getInteracting() != null) { - Actor interactingNpc = Microbot.getClient().getLocalPlayer().getInteracting(); - if (interactingNpc.getWorldLocation().equals(specialRoot.getWorldLocation())) { - continue; - } - } + if (Rs2Player.isAnimating()) { + continue; // Already chopping; avoid redundant clicks + }Repeat the same replacement in the regular root branch.
Also applies to: 74-80
88-89: Align execute() return with other eventsOther events return
trueon completion; this one returnsfalse. If the framework interprets the boolean, this inconsistency may affect control flow.- return false; + return true;runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/Forestry/EntlingsEvent.java (2)
35-40: Sort then act is good; consider broadening ID filterOnly
NpcID.GATHERING_EVENT_ENTLINGS_NPC_01is included. If other variant IDs (e.g.,_02,_03) exist, the handler will miss them.I can provide a small helper to match a Set of entling IDs if you confirm the constants in your codebase.
45-59: Handle unrecognized/no overhead text with a short backoffWhen all entlings have null/unrecognized overheads, the loop spins hot. Add a tiny sleep before the next iteration if nothing actionable was found.
for (Rs2NpcModel entling : entlings) { String request = entling.getOverheadText(); String action; if (request == null || request.isEmpty()) { - continue; // Skip if no overhead text is present + continue; // Skip if no overhead text is present } switch (request) { case "Breezy at the back!": case "Short back and sides!": action = "Prune-back"; break; case "A leafy mullet!": case "Short on top!": action = "Prune-top"; break; default: - continue; + continue; } Microbot.log("EntlingsEvent: Interacting with entling: with action: " + action); Rs2Npc.interact(entling, action); Rs2Player.waitForAnimation(1000); // Wait for the pruning animation to finish } + // Avoid busy-spin when no actionable entlings were found + net.runelite.client.plugins.microbot.util.Global.sleep(150);runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/Forestry/RitualEvent.java (3)
40-42: Remove unnecessary continue statementThe
continuestatement on line 42 is redundant since there's no code after it in the loop. The loop will naturally continue to the next iteration.if (targetCircle == null) { Microbot.log("RitualEvent: No target circle found, cannot proceed with the ritual.", Level.INFO); - continue; // If no target circle is found, we cannot proceed with the event }
44-48: Consider improving wait condition logicThe TODO comment indicates there's a better condition to wait for when already at the target circle. Consider waiting for a specific game state change (e.g., circle activation or ritual progress) rather than using a fixed sleep duration.
Would you like me to help implement a better wait condition based on ritual circle state changes?
64-89: Document the ritual circle solving algorithmThe XOR-based algorithm for solving ritual circles lacks documentation. Adding comments would improve maintainability by explaining the bit manipulation logic and the pattern matching approach.
private NPC solveCircles(List<NPC> ritualCircles) { if (ritualCircles.size() != 5) { return null; } + // Build a bitmask representing all circles using XOR + // Each circle is encoded as: (16 << shape) | (1 << color) + // where shape is derived from dividing the NPC offset by 4 + // and color is the remainder when dividing by 4 int s = 0; for (NPC npc : ritualCircles) { int off = npc.getId() - NpcID.GATHERING_EVENT_ENCHANTED_RITUAL_A_1; int shape = off / 4; int color = off % 4; int id = (16 << shape) | (1 << color); s = s ^ id; // XOR operation } + // Find the circle whose ID is a subset of the XOR mask + // This identifies the unique/odd circle that needs to be stood on for (NPC npc : ritualCircles) { int off = npc.getId() - NpcID.GATHERING_EVENT_ENCHANTED_RITUAL_A_1; int shape = off / 4; int color = off % 4; int id = (16 << shape) | (1 << color); if ((id & s) == id) { // Bitwise AND return npc; } } return null; }runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/Forestry/FlowersEvent.java (2)
29-34: Implement the execute() methodThe
execute()method is currently a stub that always returnsfalse. This needs implementation to handle the flowers event properly.Would you like me to help implement the flowers event execution logic based on the pattern used in other forestry events?
41-51: Consider using a Set for O(1) lookup performanceThe
isFloweringBushmethod performs multiple equality checks. For better performance, consider using a Set for O(1) lookup time.+private static final Set<Integer> FLOWERING_BUSH_IDS = Set.of( + NpcID.GATHERING_EVENT_FLOWERING_TREE_BUSH_COL01, + NpcID.GATHERING_EVENT_FLOWERING_TREE_BUSH_COL02, + NpcID.GATHERING_EVENT_FLOWERING_TREE_BUSH_COL03, + NpcID.GATHERING_EVENT_FLOWERING_TREE_BUSH_COL04, + NpcID.GATHERING_EVENT_FLOWERING_TREE_BUSH_COL05, + NpcID.GATHERING_EVENT_FLOWERING_TREE_BUSH_COL06, + NpcID.GATHERING_EVENT_FLOWERING_TREE_BUSH_COL07, + NpcID.GATHERING_EVENT_FLOWERING_TREE_BUSH_COL08 +); + private static boolean isFloweringBush(int npcId) { - return npcId == NpcID.GATHERING_EVENT_FLOWERING_TREE_BUSH_COL01 || - npcId == NpcID.GATHERING_EVENT_FLOWERING_TREE_BUSH_COL02 || - npcId == NpcID.GATHERING_EVENT_FLOWERING_TREE_BUSH_COL03 || - npcId == NpcID.GATHERING_EVENT_FLOWERING_TREE_BUSH_COL04 || - npcId == NpcID.GATHERING_EVENT_FLOWERING_TREE_BUSH_COL05 || - npcId == NpcID.GATHERING_EVENT_FLOWERING_TREE_BUSH_COL06 || - npcId == NpcID.GATHERING_EVENT_FLOWERING_TREE_BUSH_COL07 || - npcId == NpcID.GATHERING_EVENT_FLOWERING_TREE_BUSH_COL08; + return FLOWERING_BUSH_IDS.contains(npcId); }runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/Forestry/HivesEvent.java (2)
29-30: Simplify stream usage for beehive validationThe stream operation can be simplified using
anyMatchinstead offindAny().isPresent().var beehives = Rs2Npc.getNpcs(x -> x.getId() == net.runelite.api.gameval.NpcID.GATHERING_EVENT_BEES_BEEBOX_1 || x.getId() == net.runelite.api.gameval.NpcID.GATHERING_EVENT_BEES_BEEBOX_2); -return beehives.findAny().isPresent() && Rs2Inventory.count(plugin.config.TREE().getLogID()) > 1; +return beehives.anyMatch(x -> true) && Rs2Inventory.count(plugin.config.TREE().getLogID()) > 1;
52-53: Implement TODO: Handle sturdy Beehive partsThe TODO comment indicates that sturdy Beehive parts should be dropped from inventory if present. This functionality should be implemented for complete event handling.
Would you like me to generate code to drop sturdy Beehive parts from the inventory after building beehives?
runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/Forestry/StrugglingSaplingEvent.java (4)
104-110: Add null check before accessing ingredient IDThe code accesses
ingredient.getId()without verifying thatingredientis not null. While unlikely in this context, adding a null check would make the code more defensive.// Look for matching ingredient in our available ingredients for (GameObject ingredient : ingredients) { + if (ingredient == null) continue; if (ingredient.getId() == correctIngredient.getId()) { // Collect this ingredient as it's known to be correct Microbot.log("StrugglingSaplingEvent: Collecting known correct ingredient: " + ingredient.getWorldLocation()); Rs2GameObject.interact(ingredient, "Collect"); Rs2Player.waitForAnimation(); } }
129-130: Use secure random for better randomnessUsing
Math.random()for randomization is less ideal than using a proper random generator. Consider usingThreadLocalRandomfor better randomness.+import java.util.concurrent.ThreadLocalRandom; Microbot.log("StrugglingSaplingEvent: No known correct ingredient, collecting a random one."); -var randomIngredient = availableIngredients.get((int) (Math.random() * availableIngredients.size())); +var randomIngredient = availableIngredients.get(ThreadLocalRandom.current().nextInt(availableIngredients.size()));
136-138: Consider using Arrays.fill for cleaner array resetInstead of individually setting each element to null, consider using
Arrays.fillfor a cleaner approach.Microbot.log("StrugglingSaplingEvent: Finished processing struggling sapling."); -plugin.saplingOrder[0] = null; // Reset the sapling order after processing -plugin.saplingOrder[1] = null; -plugin.saplingOrder[2] = null; +Arrays.fill(plugin.saplingOrder, null); // Reset the sapling order after processing
141-144: Improve exception loggingThe current exception logging concatenates the stack trace with the message using string concatenation. Consider using the logger's built-in exception handling for better formatting.
catch (Exception e) { - Microbot.log("StrugglingSaplingEvent: Error during execution: " + e.getMessage() + Arrays.toString(e.getStackTrace())); + Microbot.log("StrugglingSaplingEvent: Error during execution", e); return false; }runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/AutoWoodcuttingScript.java (4)
82-87: Nit: prefer enum identity (==) over equals for readability and safety.Comparing enums via
==is idiomatic and null-safe. Current code uses.equals(...)forwalkBack()andTREE()checks.- if (config.walkBack().equals(WoodcuttingWalkBack.LAST_LOCATION)) { + if (config.walkBack() == WoodcuttingWalkBack.LAST_LOCATION) { - tree = Rs2GameObject.findReachableObject(config.TREE().getName(), true, config.distanceToStray(), getInitialPlayerLocation(), config.TREE().equals(WoodcuttingTree.REDWOOD), config.TREE().getAction()); + tree = Rs2GameObject.findReachableObject( + config.TREE().getName(), + true, + config.distanceToStray(), + getInitialPlayerLocation(), + config.TREE() == WoodcuttingTree.REDWOOD, + config.TREE().getAction()); - if (config.walkBack().equals(WoodcuttingWalkBack.LAST_LOCATION)) { + if (config.walkBack() == WoodcuttingWalkBack.LAST_LOCATION) {Also applies to: 129-131, 137-140
297-339: Reduce chances of hangs and double-wait in burnLog.Two issues:
sleepUntil(() -> !isFiremake())has no timeout; if conditions never change, the script thread can stall.- Wrapping
Rs2Player.waitForXpDrop(...)(which itself waits) insidesleepUntiladds redundant waiting.- sleepUntil(() -> !isFiremake()); + // Avoid indefinite waits; bail out if no state change within 6 seconds + sleepUntil(() -> !isFiremake(), 6000); - if (!cannotLightFire && isFiremake()) { - sleepUntil(() -> Rs2Player.waitForXpDrop(Skill.FIREMAKING, 40000), 40000); - } + if (!cannotLightFire && isFiremake()) { + // Block until the next FM XP drop or timeout + Rs2Player.waitForXpDrop(Skill.FIREMAKING, 40000); + }Also consider adding a recursion cap in
fireSpot(int distance)to prevent deep recursion in extreme cases (e.g., max distance 30) and returning to a simple nearby tile if exceeded.
64-79: Bank session may remain open after withdrawing tinderbox.When tinderbox is missing, you open the bank and withdraw it but never explicitly close the bank if logs are already present. This leaves the bank UI open unnecessarily.
if (!Rs2Inventory.hasItem(TINDERBOX)) { Rs2Bank.openBank(); sleepUntil(Rs2Bank::isOpen, 20000); Rs2Bank.withdrawItem(true, "Tinderbox"); + // Close if we don't also need to withdraw logs + if (Rs2Inventory.hasItem(config.TREE().getLog())) { + Rs2Bank.closeBank(); + sleep(300, 700); + } }
371-381: Rename typo inisFlectchingmethod nameThe method
isFlectchingand its single call site (sleepUntil(() -> !isFlectching(), 5000);) both contain a minor spelling error. Renaming it toisFletchingwill improve readability and consistency.• Location:
- Definition at
AutoWoodcuttingScript.java:379- Call site at
AutoWoodcuttingScript.java:376Apply this optional refactor:
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/AutoWoodcuttingScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/AutoWoodcuttingScript.java @@ -373,9 +373,9 @@ private void fletchArrowShaft(AutoWoodcuttingConfig config) { Rs2Inventory.combineClosest("knife", config.TREE().getLog()); sleepUntil(Rs2Widget::isProductionWidgetOpen, 5000); Rs2Widget.clickWidget("arrow shafts"); Rs2Player.waitForAnimation(); - sleepUntil(() -> !isFlectching(), 5000); + sleepUntil(() -> !isFletching(), 5000); } - private boolean isFlectching() { + private boolean isFletching() { return Rs2Player.isAnimating(3000) && Rs2Player.getLastAnimationID() == AnimationID.FLETCHING_BOW_CUTTING; }runelite-client/src/main/java/net/runelite/client/plugins/microbot/externalplugins/MicrobotPluginManager.java (1)
100-116: Optional: atomic manifest swap to avoid transient empty reads.Clearing and repopulating a shared map can briefly expose an empty state to readers. Swap the reference atomically to a new
ConcurrentHashMapto avoid transient inconsistencies.- if (changed) - { - manifestMap.clear(); - manifestMap.putAll(next); + if (changed) + { + var replacement = new ConcurrentHashMap<>(next); + manifestMap.clear(); + manifestMap.putAll(replacement);Alternatively, wrap the map in an
AtomicReference<Map<String,MicrobotPluginManifest>>and replace the reference.runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/AutoWoodcuttingConfig.java (2)
154-163: User-facing typo: “Comma separated” fix and minor clarity.Same spelling issue and a chance to clarify phrasing.
- name = "Items to keep when dropping (Comma separated)", - description = "Items to keep in inventory", + name = "Items to keep when dropping (comma separated)", + description = "Items to keep in your inventory while dropping",
68-71: Consistency: config method names are verbs; consider prefixing booleans with is/enable.Optional style note:
HardwoodTreePatch()/lootBirdNests()/lootSeeds()could follow a consistent boolean naming convention (e.g.,hardwoodTreePatch()orisHardwoodTreePatchEnabled()).Also applies to: 79-82, 90-93
runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/AutoWoodcuttingPlugin.java (2)
37-41: Reduce surface area: avoid public field + Lombok getter for the script.
autoWoodcuttingScriptispublicand also annotated with@Getter(AccessLevel.MODULE). Prefer one approach. Making the fieldprivatewith a package/module-level getter reduces accidental external mutation.- @Getter(AccessLevel.MODULE) - public AutoWoodcuttingScript autoWoodcuttingScript; + @Getter(AccessLevel.MODULE) + private AutoWoodcuttingScript autoWoodcuttingScript;
276-295: Simplify config boolean toggles in onConfigChanged.Comparing strings
"true"/"false"is brittle. UseBoolean.parseBoolean(value).- var value = ev.getNewValue(); - if (value != null && value.equals("true")) { + var value = ev.getNewValue(); + if (Boolean.parseBoolean(value)) { this.addEvent(key); - } - else if (value != null && value.equals("false")) { + } else if (value != null && !Boolean.parseBoolean(value)) { this.removeEvent(key); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (20)
runelite-client/pom.xml(1 hunks)runelite-client/src/main/java/net/runelite/client/plugins/microbot/externalplugins/MicrobotPluginManager.java(6 hunks)runelite-client/src/main/java/net/runelite/client/plugins/microbot/ui/MicrobotPluginHubPanel.java(3 hunks)runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/bank/Rs2Bank.java(1 hunks)runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/reflection/Rs2Reflection.java(2 hunks)runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/AutoWoodcuttingConfig.java(7 hunks)runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/AutoWoodcuttingOverlay.java(1 hunks)runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/AutoWoodcuttingPlugin.java(2 hunks)runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/AutoWoodcuttingScript.java(7 hunks)runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/Forestry/EggEvent.java(1 hunks)runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/Forestry/EntlingsEvent.java(1 hunks)runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/Forestry/FlowersEvent.java(1 hunks)runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/Forestry/FoxEvent.java(1 hunks)runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/Forestry/HivesEvent.java(1 hunks)runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/Forestry/LeprechaunEvent.java(1 hunks)runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/Forestry/RitualEvent.java(1 hunks)runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/Forestry/RootEvent.java(1 hunks)runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/Forestry/StrugglingSaplingEvent.java(1 hunks)runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/enums/ForestryEvents.java(1 hunks)runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/enums/WoodcuttingScriptState.java(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (12)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/Forestry/RootEvent.java (4)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/combat/Rs2Combat.java (1)
Rs2Combat(27-244)runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/gameobject/Rs2GameObject.java (1)
Rs2GameObject(36-2002)runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/player/Rs2Player.java (1)
Rs2Player(67-2062)runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/Global.java (1)
Global(10-171)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/Forestry/EggEvent.java (5)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/dialogues/Rs2Dialogue.java (1)
Rs2Dialogue(18-725)runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/gameobject/Rs2GameObject.java (1)
Rs2GameObject(36-2002)runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/inventory/Rs2Inventory.java (1)
Rs2Inventory(60-2244)runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/player/Rs2Player.java (1)
Rs2Player(67-2062)runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/Global.java (1)
Global(10-171)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/AutoWoodcuttingOverlay.java (2)
runelite-api/src/main/java/net/runelite/api/Perspective.java (1)
Perspective(63-1014)runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/AutoWoodcuttingScript.java (1)
AutoWoodcuttingScript(36-396)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/Forestry/FoxEvent.java (1)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/player/Rs2Player.java (1)
Rs2Player(67-2062)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/Forestry/LeprechaunEvent.java (2)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/Global.java (1)
Global(10-171)runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/player/Rs2Player.java (1)
Rs2Player(67-2062)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/Forestry/HivesEvent.java (4)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/inventory/Rs2Inventory.java (1)
Rs2Inventory(60-2244)runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/keyboard/Rs2Keyboard.java (1)
Rs2Keyboard(15-174)runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/player/Rs2Player.java (1)
Rs2Player(67-2062)runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/Global.java (1)
Global(10-171)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/Forestry/StrugglingSaplingEvent.java (5)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/gameobject/Rs2GameObject.java (1)
Rs2GameObject(36-2002)runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/inventory/Rs2Inventory.java (1)
Rs2Inventory(60-2244)runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/player/Rs2Player.java (1)
Rs2Player(67-2062)runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/AutoWoodcuttingScript.java (1)
AutoWoodcuttingScript(36-396)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/woodcutting/Forestry/EntlingsEvent.java (1)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/player/Rs2Player.java (1)
Rs2Player(67-2062)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/Forestry/RitualEvent.java (2)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/player/Rs2Player.java (1)
Rs2Player(67-2062)runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/Global.java (1)
Global(10-171)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/externalplugins/MicrobotPluginManager.java (1)
runelite-client/src/main/java/net/runelite/client/RuneLiteProperties.java (1)
RuneLiteProperties(35-162)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/AutoWoodcuttingScript.java (7)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/combat/Rs2Combat.java (1)
Rs2Combat(27-244)runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/equipment/Rs2Equipment.java (1)
Rs2Equipment(27-485)runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/gameobject/Rs2GameObject.java (1)
Rs2GameObject(36-2002)runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/grounditem/LootingParameters.java (1)
LootingParameters(3-115)runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/inventory/Rs2Inventory.java (1)
Rs2Inventory(60-2244)runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/player/Rs2Player.java (1)
Rs2Player(67-2062)runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/keyboard/Rs2Keyboard.java (1)
Rs2Keyboard(15-174)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/AutoWoodcuttingPlugin.java (1)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/gameobject/Rs2GameObject.java (1)
Rs2GameObject(36-2002)
⏰ 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). (1)
- GitHub Check: Build
🔇 Additional comments (12)
runelite-client/pom.xml (1)
44-44: Shaded artifact version verified; please update docsI’ve confirmed that the Maven shade plugin in runelite-client/pom.xml is correctly producing target/microbot-1.9.8.8.jar using the updated
<microbot.version>property. No otheroutputFileentries were found, so the artifact naming is consistent.• Please review and update any downstream artifacts or documentation (e.g., README) that reference the Microbot version.
• Once documentation is aligned, this change can be finalized.runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/bank/Rs2Bank.java (1)
2214-2221: emptyLogBasket helper verified and approved
TheLOG_BASKET_CLOSEDandLOG_BASKET_OPENconstants are present inrunelite-api/src/main/java/net/runelite/api/gameval/ItemID.java(lines 71485 and 71490), so the newemptyLogBasket()implementation correctly reuses the existingempty(...)helper.runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/enums/WoodcuttingScriptState.java (1)
3-7: Enum addition is clear and self-explanatory.Names align with usage in state machines and overlays.
runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/enums/ForestryEvents.java (1)
3-13: ForestryEvents enum looks good; covers the expected event set.Names are consistent and easy to render in UI. No issues spotted.
runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/Forestry/FoxEvent.java (1)
3-3: Import Path Validated – No Changes RequiredI’ve confirmed that your
runelite-apimodule includes both
net.runelite.api.NpcIDandnet.runelite.api.gameval.NpcID(and the correspondingObjectIDin each package), so allimport net.runelite.api.gameval.NpcID;(andObjectID) usages are valid and will compile as-is:
- runelite-api/src/main/java/net/runelite/api/gameval/NpcID.java
- runelite-api/src/main/java/net/runelite/api/gameval/ObjectID.java
Since
net.runelite.api.gamevalis present and populated with the expected classes, no modifications to the import paths are necessary.runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/Forestry/LeprechaunEvent.java (1)
3-5: Verify import paths (gameval NpcID/ObjectID)Double-check the availability of
net.runelite.api.gameval.NpcIDandnet.runelite.api.gameval.ObjectIDin your repo. If not present, usenet.runelite.api.NpcID/net.runelite.api.ObjectID.You can reuse the script from my FoxEvent comment to verify.
runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/Forestry/RootEvent.java (1)
52-54: Verify special-attack energy requirement (1000) is correct
setSpecState(true, 1000)requires 100% special energy (RuneLite varp is x10). Most axes use less. Confirm this value matches the equipped axe; otherwise the special may never fire. ConsidersetSpecState(true)or making the requirement configurable.runelite-client/src/main/java/net/runelite/client/plugins/microbot/ui/MicrobotPluginHubPanel.java (3)
574-610: LGTM! Improved manifest retrieval and error handlingThe refactored
reloadPluginList()method now properly uses the cached manifest fromMicrobotPluginManagerinstead of downloading directly, and provides better error handling with a retry option specifically for plugin counts.
137-167: Well-implemented image scaling for plugin iconsThe
scaleImageToFitmethod properly scales images while maintaining aspect ratio and uses high-quality rendering hints for better visual results.
723-726: Good integration with manifest cache updatesThe subscription to
ExternalPluginsChangedevents ensures the UI stays synchronized with manifest cache changes fromMicrobotPluginManager.runelite-client/src/main/java/net/runelite/client/plugins/microbot/externalplugins/MicrobotPluginManager.java (1)
397-420: Ensure consistent identifiers between installed list andPluginDescriptor.name()
ThesyncPlugins()method builds a map of loaded plugins keyed by the human-readablePluginDescriptor.name()(e.g. “Entity Hider”) but then checks membership against entries fromgetInstalledPlugins(), which—based on the missingplugins.jsonin the repo and typical installation logic—almost certainly contains jar/filename identifiers (e.g.entity-hider, artifact IDs, or class names), not the descriptor’s display names. This mismatch means plugins will be spuriously removed or retained.Please verify:
- That
microbot-plugins/plugins.json(or wherevergetInstalledPlugins()reads from) stores the exact descriptor names (PluginDescriptor.name()), including whitespace and casing.- If it stores filenames or other IDs, update
syncPlugins()to either
- normalize filenames to descriptor names (e.g., read the plugin jar’s annotation to map file → descriptor), or
- switch to using a consistent internal identifier (for example an explicit
idfield in the descriptor) for both installation and lookup.Location to investigate:
• runelite-client/src/main/java/net/runelite/client/plugins/microbot/externalplugins/MicrobotPluginManager.java (around lines 397–420)runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/AutoWoodcuttingPlugin.java (1)
69-77: Startup order: register forestry events before starting the script is good—retain this.Events are added (when enabled) before
autoWoodcuttingScript.run(config), so the script can react to event-driven state immediately. Looks good.
| try (Response response = okHttpClient.newCall(request).execute()) | ||
| { | ||
| if (!response.isSuccessful()) | ||
| { | ||
| log.error("Error downloading plugin: {}, code: {}", manifest.getInternalName(), response.code()); | ||
| return; | ||
| } | ||
|
|
||
| byte[] jarData = response.body().bytes(); | ||
|
|
There was a problem hiding this comment.
Null-check Response.body() to avoid NPE during plugin download.
response.body() can be null; calling .bytes() would NPE and mask the actual HTTP failure.
- try (Response response = okHttpClient.newCall(request).execute())
+ try (Response response = okHttpClient.newCall(request).execute())
{
if (!response.isSuccessful())
{
log.error("Error downloading plugin: {}, code: {}", manifest.getInternalName(), response.code());
return;
}
- byte[] jarData = response.body().bytes();
+ if (response.body() == null) {
+ log.error("Empty response body when downloading plugin: {}", manifest.getInternalName());
+ return;
+ }
+ byte[] jarData = response.body().bytes();📝 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.
| try (Response response = okHttpClient.newCall(request).execute()) | |
| { | |
| if (!response.isSuccessful()) | |
| { | |
| log.error("Error downloading plugin: {}, code: {}", manifest.getInternalName(), response.code()); | |
| return; | |
| } | |
| byte[] jarData = response.body().bytes(); | |
| try (Response response = okHttpClient.newCall(request).execute()) | |
| { | |
| if (!response.isSuccessful()) | |
| { | |
| log.error("Error downloading plugin: {}, code: {}", manifest.getInternalName(), response.code()); | |
| return; | |
| } | |
| if (response.body() == null) | |
| { | |
| log.error("Empty response body when downloading plugin: {}", manifest.getInternalName()); | |
| return; | |
| } | |
| byte[] jarData = response.body().bytes(); |
🤖 Prompt for AI Agents
In
runelite-client/src/main/java/net/runelite/client/plugins/microbot/externalplugins/MicrobotPluginManager.java
around lines 234 to 243, the code calls response.body().bytes() without
null-checking the body; add a null-check for response.body() after verifying
response.isSuccessful(), log an appropriate error (including manifest name and
response.code() and/or response.message()) and return if the body is null to
avoid an NPE, otherwise proceed to read bytes.
| if (Microbot.isLoggedIn() && !firstRun) { | ||
| resetStats(); | ||
| firstRun = true; | ||
| } | ||
| try { |
There was a problem hiding this comment.
Fix: avoid NPE before first login and reset on logout.
startTime is null until the first logged-in render. Later stats code assumes it’s non-null. On startup while logged out this throws and floods logs (caught by try/catch). Also, if you log out, firstRun never resets.
public Dimension render(Graphics2D graphics) {
- if (Microbot.isLoggedIn() && !firstRun) {
+ if (!Microbot.isLoggedIn()) {
+ // ensure we re-initialize after re-login
+ firstRun = false;
+ } else if (!firstRun) {
resetStats();
firstRun = true;
}📝 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.
| if (Microbot.isLoggedIn() && !firstRun) { | |
| resetStats(); | |
| firstRun = true; | |
| } | |
| try { | |
| public Dimension render(Graphics2D graphics) { | |
| - if (Microbot.isLoggedIn() && !firstRun) { | |
| - resetStats(); | |
| - firstRun = true; | |
| if (!Microbot.isLoggedIn()) { | |
| // ensure we re-initialize after re-login | |
| firstRun = false; | |
| } else if (!firstRun) { | |
| resetStats(); | |
| firstRun = true; | |
| } | |
| try { | |
| … |
🤖 Prompt for AI Agents
In
runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/AutoWoodcuttingOverlay.java
around lines 62-66, avoid a NullPointerException from startTime being null
before the first login and ensure firstRun resets on logout: only reference or
reset stats/startTime when Microbot.isLoggedIn() is true (initialize startTime
at first successful login), and when Microbot.isLoggedIn() becomes false clear
firstRun and startTime so subsequent logins reinitialize; update the conditional
logic to check login state before using startTime and to reset
firstRun/startTime on logout.
| // Calculate XP per hour | ||
| long secondsElapsed = Duration.between(startTime, Instant.now()).getSeconds(); | ||
| if (secondsElapsed > 0) { | ||
| double xpPerHour = (double) xpGained / secondsElapsed * 3600; | ||
| panelComponent.getChildren().add(LineComponent.builder() | ||
| .left("XP/Hour:") | ||
| .right(NumberFormat.getInstance().format((long) xpPerHour)) | ||
| .rightColor(NORMAL_TEXT_COLOR) | ||
| .build()); | ||
| } | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion
Guard XP/hour computation when startTime is not initialized.
When not logged in (or before first run), startTime can be null.
- long secondsElapsed = Duration.between(startTime, Instant.now()).getSeconds();
- if (secondsElapsed > 0) {
+ Long secondsElapsed = (startTime != null) ? Duration.between(startTime, Instant.now()).getSeconds() : null;
+ if (secondsElapsed != null && secondsElapsed > 0) {
double xpPerHour = (double) xpGained / secondsElapsed * 3600;
panelComponent.getChildren().add(LineComponent.builder()
.left("XP/Hour:")
.right(NumberFormat.getInstance().format((long) xpPerHour))
.rightColor(NORMAL_TEXT_COLOR)
.build());
}📝 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.
| // Calculate XP per hour | |
| long secondsElapsed = Duration.between(startTime, Instant.now()).getSeconds(); | |
| if (secondsElapsed > 0) { | |
| double xpPerHour = (double) xpGained / secondsElapsed * 3600; | |
| panelComponent.getChildren().add(LineComponent.builder() | |
| .left("XP/Hour:") | |
| .right(NumberFormat.getInstance().format((long) xpPerHour)) | |
| .rightColor(NORMAL_TEXT_COLOR) | |
| .build()); | |
| } | |
| // Calculate XP per hour | |
| Long secondsElapsed = (startTime != null) | |
| ? Duration.between(startTime, Instant.now()).getSeconds() | |
| : null; | |
| if (secondsElapsed != null && secondsElapsed > 0) { | |
| double xpPerHour = (double) xpGained / secondsElapsed * 3600; | |
| panelComponent.getChildren().add(LineComponent.builder() | |
| .left("XP/Hour:") | |
| .right(NumberFormat.getInstance().format((long) xpPerHour)) | |
| .rightColor(NORMAL_TEXT_COLOR) | |
| .build()); | |
| } |
🤖 Prompt for AI Agents
In
runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/AutoWoodcuttingOverlay.java
around lines 126 to 136, the XP/hour computation calls
Duration.between(startTime, Instant.now()) without ensuring startTime is
initialized which can cause an NPE; guard this by checking startTime != null
before computing Duration (and only compute xpPerHour if startTime is non-null
and secondsElapsed > 0), or alternatively initialize startTime when the plugin
starts so the Duration call is always safe.
| panelComponent.getChildren().add(LineComponent.builder() | ||
| .left("Time Running:") | ||
| .right(formatDuration(Duration.between(startTime, Instant.now()))) | ||
| .rightColor(NORMAL_TEXT_COLOR) | ||
| .build()); | ||
|
|
There was a problem hiding this comment.
Guard Time Running formatting when startTime is null.
Avoid Duration.between(null, ...) NPE.
- panelComponent.getChildren().add(LineComponent.builder()
- .left("Time Running:")
- .right(formatDuration(Duration.between(startTime, Instant.now())))
- .rightColor(NORMAL_TEXT_COLOR)
- .build());
+ panelComponent.getChildren().add(LineComponent.builder()
+ .left("Time Running:")
+ .right(startTime != null ? formatDuration(Duration.between(startTime, Instant.now())) : "00:00:00")
+ .rightColor(NORMAL_TEXT_COLOR)
+ .build());📝 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.
| panelComponent.getChildren().add(LineComponent.builder() | |
| .left("Time Running:") | |
| .right(formatDuration(Duration.between(startTime, Instant.now()))) | |
| .rightColor(NORMAL_TEXT_COLOR) | |
| .build()); | |
| panelComponent.getChildren().add(LineComponent.builder() | |
| .left("Time Running:") | |
| .right(startTime != null | |
| ? formatDuration(Duration.between(startTime, Instant.now())) | |
| : "00:00:00") | |
| .rightColor(NORMAL_TEXT_COLOR) | |
| .build()); |
🤖 Prompt for AI Agents
In
runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/AutoWoodcuttingOverlay.java
around lines 145-150, guard against a null startTime before calling
Duration.between to avoid an NPE: check if startTime is null and, if so, set the
right text to a safe placeholder (e.g., "-" or "Not started") and the rightColor
as before; otherwise compute formatDuration(Duration.between(startTime,
Instant.now())). Ensure the conditional supplies the same LineComponent builder
fields so the panel rendering remains consistent.
| if (msg.equals("you can't light a fire here.")) { | ||
| autoWoodcuttingScript.cannotLightFire = true; | ||
| } | ||
|
|
There was a problem hiding this comment.
Fix: exact string match for “You can't light a fire here.” is case-sensitive.
OSRS chat starts with capital “You”. The lowercase comparison will fail, leaving cannotLightFire unset and causing the burn logic not to relocate.
- if (msg.equals("you can't light a fire here.")) {
+ if (msg.equalsIgnoreCase("You can't light a fire here.")) {
autoWoodcuttingScript.cannotLightFire = true;
}Optionally, use a case-insensitive contains to be resilient across locales/punctuation:
- if (msg.equals("you can't light a fire here.")) {
+ if (msg.toLowerCase(Locale.ROOT).contains("can't light a fire here")) {📝 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.
| if (msg.equals("you can't light a fire here.")) { | |
| autoWoodcuttingScript.cannotLightFire = true; | |
| } | |
| if (msg.equalsIgnoreCase("You can't light a fire here.")) { | |
| autoWoodcuttingScript.cannotLightFire = true; | |
| } |
| .filter(nest -> pheasants.stream() | ||
| .noneMatch(pheasant -> pheasant.getWorldLocation() == nest.getWorldLocation())) | ||
| .collect(Collectors.toList()); | ||
|
|
There was a problem hiding this comment.
Fix WorldPoint comparison: use equals() instead of ==
== compares references, not values, so the “nest without nearby pheasants” filter is incorrect and may misclassify nests. Use equals.
- .filter(nest -> pheasants.stream()
- .noneMatch(pheasant -> pheasant.getWorldLocation() == nest.getWorldLocation()))
+ .filter(nest -> pheasants.stream()
+ .noneMatch(pheasant -> pheasant.getWorldLocation().equals(nest.getWorldLocation())))📝 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.
| .filter(nest -> pheasants.stream() | |
| .noneMatch(pheasant -> pheasant.getWorldLocation() == nest.getWorldLocation())) | |
| .collect(Collectors.toList()); | |
| .filter(nest -> pheasants.stream() | |
| .noneMatch(pheasant -> pheasant.getWorldLocation().equals(nest.getWorldLocation()))) | |
| .collect(Collectors.toList()); |
🤖 Prompt for AI Agents
In
runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/Forestry/EggEvent.java
around lines 81 to 84, the filter uses '==' to compare WorldPoint instances
which compares references; replace this with a value comparison using equals to
correctly detect matching locations and avoid misclassification — e.g., change
the predicate to compare
pheasant.getWorldLocation().equals(nest.getWorldLocation()) or use
Objects.equals(pheasant.getWorldLocation(), nest.getWorldLocation()) to guard
against nulls.
| public boolean validate() { | ||
| var flowers = Rs2Npc.getNpcs(npc -> | ||
| npc.getName() != null && isFloweringBush(npc.getId()) | ||
| ).collect(Collectors.toList()); | ||
| if (flowers.isEmpty()) { | ||
| return false; // No flowering bushes found, cannot proceed with the event | ||
| } | ||
| return false; | ||
| } |
There was a problem hiding this comment.
Fix the validate() method to return true when flowers are found
The validate() method always returns false even when flowering bushes are found. This prevents the event from ever executing.
@Override
public boolean validate() {
var flowers = Rs2Npc.getNpcs(npc ->
npc.getName() != null && isFloweringBush(npc.getId())
).collect(Collectors.toList());
if (flowers.isEmpty()) {
return false; // No flowering bushes found, cannot proceed with the event
}
- return false;
+ return true;
}📝 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.
| public boolean validate() { | |
| var flowers = Rs2Npc.getNpcs(npc -> | |
| npc.getName() != null && isFloweringBush(npc.getId()) | |
| ).collect(Collectors.toList()); | |
| if (flowers.isEmpty()) { | |
| return false; // No flowering bushes found, cannot proceed with the event | |
| } | |
| return false; | |
| } | |
| @Override | |
| public boolean validate() { | |
| var flowers = Rs2Npc.getNpcs(npc -> | |
| npc.getName() != null && isFloweringBush(npc.getId()) | |
| ).collect(Collectors.toList()); | |
| if (flowers.isEmpty()) { | |
| return false; // No flowering bushes found, cannot proceed with the event | |
| } | |
| return true; | |
| } |
🤖 Prompt for AI Agents
In
runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/Forestry/FlowersEvent.java
around lines 19 to 27, the validate() method collects flowering bushes into
'flowers' but always returns false; change the final return so the method
returns true when flowers are found (e.g., return true inside the non-empty
branch or simply return !flowers.isEmpty()) so the event can proceed when
flowering bushes exist.
| while (this.validate()) { | ||
| var trap = Rs2Npc.getNpc(NpcID.GATHERING_EVENT_POACHERS_TRAP); | ||
| if (trap == null) { | ||
| continue; // If the trap is not found, we cannot proceed with the event | ||
| } | ||
| Microbot.log("FoxEvent: Interacting with the trap to disarm it.", Level.INFO); |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Avoid tight spin when trap is absent; throttle or wait for spawn
As written, the loop busy-spins if the trap isn’t present, pegging a CPU core. Add a short sleep or a bounded wait before continuing.
Apply this diff:
- if (trap == null) {
- continue; // If the trap is not found, we cannot proceed with the event
- }
+ if (trap == null) {
+ // If the trap is not found, wait briefly before retrying to avoid busy-spin
+ net.runelite.client.plugins.microbot.util.Global.sleep(150);
+ continue;
+ }If you prefer a bounded wait:
+ if (trap == null) {
+ net.runelite.client.plugins.microbot.util.Global.sleepUntil(
+ () -> net.runelite.client.plugins.microbot.util.npc.Rs2Npc.getNpc(
+ net.runelite.api.gameval.NpcID.GATHERING_EVENT_POACHERS_TRAP) != null, 2000);
+ continue;
+ }📝 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.
| while (this.validate()) { | |
| var trap = Rs2Npc.getNpc(NpcID.GATHERING_EVENT_POACHERS_TRAP); | |
| if (trap == null) { | |
| continue; // If the trap is not found, we cannot proceed with the event | |
| } | |
| Microbot.log("FoxEvent: Interacting with the trap to disarm it.", Level.INFO); | |
| while (this.validate()) { | |
| var trap = Rs2Npc.getNpc(NpcID.GATHERING_EVENT_POACHERS_TRAP); | |
| if (trap == null) { | |
| // If the trap is not found, wait briefly before retrying to avoid busy-spin | |
| net.runelite.client.plugins.microbot.util.Global.sleep(150); | |
| continue; | |
| } | |
| Microbot.log("FoxEvent: Interacting with the trap to disarm it.", Level.INFO); |
🤖 Prompt for AI Agents
In
runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/Forestry/FoxEvent.java
around lines 31 to 36, the while loop busy-spins when Rs2Npc.getNpc(...) returns
null; change the loop so that when trap == null you pause briefly (e.g.,
Thread.sleep(100-500 ms)) or implement a bounded wait/retry with a short timeout
before continuing, and handle InterruptedException appropriately; this will
throttle CPU usage while still allowing the trap to be detected without
spinning.
| while (this.validate()) { | ||
| var endOfRainbow = Rs2ObjectCache.getClosestObjectById(ObjectID.GATHERING_EVENT_WOODCUTTING_LEPRECHAUN_RAINBOW); | ||
| if (endOfRainbow.isEmpty()) { | ||
| continue; // If the end of the rainbow is not found, we cannot proceed with the event | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Throttle loop when rainbow object isn’t available
Without a delay, continue leads to a tight spin. Add a short sleep.
- if (endOfRainbow.isEmpty()) {
- continue; // If the end of the rainbow is not found, we cannot proceed with the event
- }
+ if (endOfRainbow.isEmpty()) {
+ // Avoid busy-spin when the rainbow hasn’t spawned yet
+ net.runelite.client.plugins.microbot.util.Global.sleep(150);
+ continue;
+ }📝 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.
| while (this.validate()) { | |
| var endOfRainbow = Rs2ObjectCache.getClosestObjectById(ObjectID.GATHERING_EVENT_WOODCUTTING_LEPRECHAUN_RAINBOW); | |
| if (endOfRainbow.isEmpty()) { | |
| continue; // If the end of the rainbow is not found, we cannot proceed with the event | |
| } | |
| while (this.validate()) { | |
| var endOfRainbow = Rs2ObjectCache.getClosestObjectById(ObjectID.GATHERING_EVENT_WOODCUTTING_LEPRECHAUN_RAINBOW); | |
| if (endOfRainbow.isEmpty()) { | |
| // Avoid busy-spin when the rainbow hasn’t spawned yet | |
| net.runelite.client.plugins.microbot.util.Global.sleep(150); | |
| continue; | |
| } |
🤖 Prompt for AI Agents
In
runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/Forestry/LeprechaunEvent.java
around lines 36-40, the while loop does a tight spin when endOfRainbow.isEmpty()
because it immediately continues without delay; add a short sleep (e.g.,
100–200ms) before continuing to throttle the loop and avoid high CPU usage, and
handle InterruptedException appropriately (restore interrupt status or break if
needed).
| sleepUntil(() -> !Rs2Player.isInteracting(), 40000); | ||
| } |
There was a problem hiding this comment.
Wait condition uses actor interaction; use animation state instead
Rs2Player.isInteracting() reflects actor targeting, which won’t change for object chopping. This makes the wait return immediately. Wait for animation to stop instead.
- Rs2Player.waitForAnimation(5000);
- sleepUntil(() -> !Rs2Player.isInteracting(), 40000);
+ Rs2Player.waitForAnimation(5000);
+ sleepUntil(() -> !Rs2Player.isAnimating(), 40000);Apply the same change in both special and regular root branches.
Also applies to: 85-86
🤖 Prompt for AI Agents
In
runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/Forestry/RootEvent.java
around lines 69-70 and 85-86, the code currently waits on
Rs2Player.isInteracting() which checks actor targeting and can return
immediately for object chopping; change the wait condition to check the player's
animation state (e.g., wait until player's animation equals -1 or not equal to
the woodcutting animation id) so the sleepUntil only completes when the chopping
animation stops; apply this replacement in both the special and regular root
branches and ensure the timeout values remain the same.
Summary by CodeRabbit
New Features
Improvements
Chores