Release Microbot 2.6.15 on RuneLite 1.12.33#1823
Conversation
screenshot() currently reads the framebuffer as RGBA bytes into a freshly allocated direct ByteBuffer, then reassembles every pixel with four relative ByteBuffer.get() calls in a nested loop. For a w×h capture that's a 4*w*h native allocation plus ~4*w*h bounds-checked byte reads per screenshot, all on the render thread. This reads the pixels with GL_BGRA / GL_UNSIGNED_INT_8_8_8_8_REV directly into the BufferedImage's backing int[] instead, then flips the rows in place with System.arraycopy. The packed-int format matches TYPE_INT_RGB's layout, and being a packed type its component order is defined relative to the int, so it's endian-independent. GL_BGRA + _REV is also the readback format drivers commonly service without a conversion pass. Both are core since OpenGL 1.2. One behavioral note: the high byte of each pixel now carries the framebuffer's alpha instead of always being 0. TYPE_INT_RGB's color model has no alpha mask, so it's ignored by rendering, getRGB(), and image encoders alike. No API change; output images are identical. Mostly motivated by requestNextFrameListener consumers that capture more than a single frame, where this path currently dominates.
[ci skip]
# Conflicts: # gradle.properties # runelite-client/pmd-ruleset.xml # runelite-client/src/main/java/net/runelite/client/plugins/config/ProfilePanel.java # runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
WalkthroughThe update bumps project versions and refreshes generated API identifiers and game-data mappings. It adds outdated-plugin runtime configuration and centralizes overlay initialization. GPU rendering gains configurable per-thread resources, deferred VAO allocation, refactored model upload helpers, updated alpha sorting, screenshot handling, and GPU memory reporting. Overlay management now supports widget-aware origins, persisted snap corners, reset and drag hooks, revised widget construction, coordinate conversion, hover selection, and anchor cycle detection. Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@runelite-client/src/main/java/net/runelite/client/callback/RenderCallbackManager.java`:
- Around line 59-61: Update the callback iteration loops in
RenderCallbackManager at the paths around callbacks.get(i) to use
snapshot-enhanced iteration over the CopyOnWriteArrayList instead of
size()/index-based access. Apply this to all three loops around lines 59, 85,
and 111, preserving the callback-specific try handling and avoiding iterator
removal during concurrent callback unregistering.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 6492a3d9-2940-4d91-ada4-5489ac152835
📒 Files selected for processing (37)
gradle.propertiesrunelite-api/src/main/java/net/runelite/api/Client.javarunelite-api/src/main/java/net/runelite/api/ItemID.javarunelite-api/src/main/java/net/runelite/api/NullItemID.javarunelite-api/src/main/java/net/runelite/api/ObjectID.javarunelite-api/src/main/java/net/runelite/api/gameval/DBTableID.javarunelite-api/src/main/java/net/runelite/api/gameval/InterfaceID.javarunelite-api/src/main/java/net/runelite/api/gameval/ItemID.javarunelite-api/src/main/java/net/runelite/api/gameval/ObjectID.javarunelite-api/src/main/java/net/runelite/api/gameval/VarPlayerID.javarunelite-api/src/main/java/net/runelite/api/gameval/VarbitID.javarunelite-client/src/main/java/net/runelite/client/RuneLite.javarunelite-client/src/main/java/net/runelite/client/RuneLiteDebug.javarunelite-client/src/main/java/net/runelite/client/RuntimeConfig.javarunelite-client/src/main/java/net/runelite/client/callback/Hooks.javarunelite-client/src/main/java/net/runelite/client/callback/RenderCallbackManager.javarunelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CoordinateClue.javarunelite-client/src/main/java/net/runelite/client/plugins/config/ProfilePanel.javarunelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.javarunelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPluginConfig.javarunelite-client/src/main/java/net/runelite/client/plugins/gpu/ModelUploader.javarunelite-client/src/main/java/net/runelite/client/plugins/gpu/SceneUploader.javarunelite-client/src/main/java/net/runelite/client/plugins/gpu/VAO.javarunelite-client/src/main/java/net/runelite/client/plugins/gpu/Zone.javarunelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPlugin.javarunelite-client/src/main/java/net/runelite/client/plugins/worldmap/FishingSpotLocation.javarunelite-client/src/main/java/net/runelite/client/plugins/worldmap/HunterAreaLocation.javarunelite-client/src/main/java/net/runelite/client/plugins/worldmap/MiningSiteLocation.javarunelite-client/src/main/java/net/runelite/client/ui/overlay/Overlay.javarunelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayManager.javarunelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayOriginX.javarunelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayOriginY.javarunelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayRenderer.javarunelite-client/src/main/java/net/runelite/client/ui/overlay/SnapCorner.javarunelite-client/src/main/java/net/runelite/client/ui/overlay/SnapCorners.javarunelite-client/src/main/java/net/runelite/client/ui/overlay/WidgetOverlay.javarunelite-client/src/main/resources/item_variations.json
💤 Files with no reviewable changes (1)
- runelite-api/src/main/java/net/runelite/api/gameval/InterfaceID.java
| for (int i = 0; i < callbacks.size(); ++i) | ||
| { | ||
| var cb = callbacks.get(i); |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Locate file =="
git ls-files | rg 'RenderCallbackManager\.java$' || true
echo "== File outline/size =="
wc -l runelite-client/src/main/java/net/runelite/client/callback/RenderCallbackManager.java
ast-grep outline runelite-client/src/main/java/net/runelite/client/callback/RenderCallbackManager.java --view compact || true
echo "== Relevant sections =="
cat -n runelite-client/src/main/java/net/runelite/client/callback/RenderCallbackManager.java | sed -n '1,150p'
echo "== Search render callback usage =="
rg -n "RenderCallbackManager|removeRenderCallback|addRenderCallback|render.*callback|callbacks" runelite-client/src/main/java -SRepository: chsami/Microbot
Length of output: 8286
🌐 Web query:
Java CopyOnWriteArrayList iterator removed from list get index bounds
💡 Result:
In Java, the iterator returned by CopyOnWriteArrayList does not support the remove method [1][2]. If you attempt to call iterator.remove, it will throw an UnsupportedOperationException [3][4]. The reason for this behavior is that CopyOnWriteArrayList iterators are designed as "snapshot" iterators [1][5]. When an iterator is created, it maintains a reference to the state of the underlying array at that specific moment [1][2]. This snapshot never changes, which is why these iterators are guaranteed not to throw a ConcurrentModificationException [1][6]. Because the iterator's view of the data is fixed to the point of its creation, it cannot modify the original list [1][4]. If you need to remove elements from a CopyOnWriteArrayList while traversing it, you should avoid using the iterator's remove method and instead use the list's own methods, such as the removeIf(Predicate) method, which is the recommended and efficient way to perform this operation [3]. Regarding "index bounds," while the iterator itself does not support removal, the CopyOnWriteArrayList class does provide a listIterator(int index) method that accepts an index [4]. If the index provided to this method is out of bounds (less than 0 or greater than the current size of the list), it will throw an IndexOutOfBoundsException [4]. Once the iterator is successfully created, however, its own internal operations (like next or previous) are constrained by the snapshot's bounds, and as noted, any attempt to call remove on that iterator will immediately throw an UnsupportedOperationException rather than an IndexOutOfBoundsException [1][4].
Citations:
- 1: https://docs.oracle.com/en/java/javase/23/docs/api/java.base/java/util/concurrent/CopyOnWriteArrayList.html
- 2: https://docs.oracle.com/en/java/javase/24/docs/api/java.base/java/util/concurrent/CopyOnWriteArrayList.html
- 3: https://stackoverflow.com/questions/57827559/why-copyonwritearraylists-iterator-allows-remove-in-enhanced-for-loop-while
- 4: https://github.com/openjdk/jdk/blob/943b87ddde7f85f5f822662fe0a03538673eb2a8/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArrayList.java
- 5: https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/concurrent/CopyOnWriteArrayList.html
- 6: https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/CopyOnWriteArrayList.html
🏁 Script executed:
#!/bin/bash
set -euo pipefail
cat -n runelite-client/src/main/java/net/runelite/client/callback/RenderCallback.java
echo
rg -n "register\(.*RenderCallback|unregister\(.*RenderCallback|addEntity\(.*Renderable|drawTile\(Scene|drawObject\(Scene" runelite-client/src/main/java -SRepository: chsami/Microbot
Length of output: 4999
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from collections.abc import Iterator
import copy
class CopyOnWriteArrayList:
def __init__(self):
self._inner = []
def add(self, item):
inner = list(self._inner)
inner.append(item)
self._inner = inner
def remove(self, item):
self._inner = list(self._inner)
self._inner.remove(item)
def size(self):
return len(self._inner)
def get(self, i):
return self._inner[i]
def __iter__(self):
return iter(list(self._inner))
calls = []
items = CopyOnWriteArrayList()
items.add("A"); items.add("B"); items.add("C")
for i in range(items.size()):
value = items.get(i)
if value == "B":
items.remove("B")
calls.append(f"get({i})={value}")
print(calls)
print("enhanced =", list(CopyOnWriteArrayList()))Repository: chsami/Microbot
Length of output: 384
🌐 Web query:
Java source CopyOnWriteArrayList Iterator remove UnsupportedOperationException
💡 Result:
The UnsupportedOperationException is thrown when calling remove, add, or set on an iterator obtained from a CopyOnWriteArrayList because these operations are explicitly unsupported by design [1][2]. The Iterator for CopyOnWriteArrayList provides a "snapshot" of the list's state at the moment the iterator was created [1][3]. Because this snapshot is immutable—referencing the fixed state of the array at that specific point in time—it cannot reflect or perform modifications to the underlying list [1][4]. Consequently, the iterator's remove method is implemented to always throw an UnsupportedOperationException [2]. To remove elements while traversing a CopyOnWriteArrayList, you should use the list's own mutation methods (e.g., list.remove(element)) or the collection's removeIf(predicate) method, rather than attempting to use the iterator's remove method [5][6]. Note that while the iterator itself is guaranteed not to throw ConcurrentModificationException because it operates on a static snapshot, modifying the list while iterating over it will not be reflected in that existing iterator [1][6].
Citations:
- 1: https://docs.oracle.com/en/java/javase/23/docs/api/java.base/java/util/concurrent/CopyOnWriteArrayList.html
- 2: https://github.com/openjdk/jdk/blob/943b87ddde7f85f5f822662fe0a03538673eb2a8/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArrayList.java
- 3: https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CopyOnWriteArrayList.html
- 4: https://stackoverflow.com/questions/45899062/why-we-cant-remove-an-element-to-copyonwritearraylist-while-iterating
- 5: https://stackoverflow.com/questions/52467653/unsupportedoperationexception-while-iterating-over-copyonwritearraylist
- 6: https://stackoverflow.com/questions/57827559/why-copyonwritearraylists-iterator-allows-remove-in-enhanced-for-loop-while
Preserve snapshot iteration for concurrent callback changes.
These loops read size() and get(i) from a CopyOnWriteArrayList; if a callback unregisters while iteration is in progress, get(i) can throw instead of being handled by the callback-specific try block. Use the snapshot-enhanced iteration instead, and keep concurrent removals off the iterator when appropriate. Applies to the loops at 59-61, 85-87, and 111-113.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@runelite-client/src/main/java/net/runelite/client/callback/RenderCallbackManager.java`
around lines 59 - 61, Update the callback iteration loops in
RenderCallbackManager at the paths around callbacks.get(i) to use
snapshot-enhanced iteration over the CopyOnWriteArrayList instead of
size()/index-based access. Apply this to all three loops around lines 59, 85,
and 111, preserving the callback-specific try handling and avoiding iterator
removal during concurrent callback unregistering.
Summary
runelite/masterthrough90eb13760d(17 upstream commits since Microbot's previous RuneLite sync).project.build.versionandrunelite.injected-client.versionto the latest released RuneLite version,1.12.33.microbot.versionfrom2.6.14to2.6.15.Conflict resolutions and compatibility
gradle.properties: retained Microbot repository/injected-client properties and set the release versions above.runelite-client/pmd-ruleset.xml: retained Microbot's intentionally relaxed PMD configuration.ProfilePanel.java: retained Microbot formatting while adopting RuneLite's new 64-profile limit.GpuPlugin.java: adopted RuneLite's render-thread refactor while retaining Microbot's safeguard that pauses scripts after a fatal GPU swap failure.RuneLiteDebug.java: migrated debug-launcher core overlay setup to the newOverlayManager.init()lifecycle.Validation
./gradlew :client:compileJava— passes (existing warnings only).