Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
93 commits
Select commit Hold shift + click to select a range
88cbddd
Merge remote-tracking branch 'origin/main' into claude/backmerge-main…
chsami Oct 13, 2026
00d5dad
Merge pull request #1958 from chsami/claude/backmerge-main-2.6.32
chsami Oct 13, 2026
f77b03c
fix(walker): report ARRIVED when a pass starts on the final path endp…
chsami Oct 13, 2026
fdf8475
Merge pull request #1959 from chsami/claude/walker-door-exit-race
chsami Oct 13, 2026
7df3833
fix(questhelper): initialise requirements before checking client meet…
chsami Oct 14, 2026
e1d4323
Merge pull request #1960 from chsami/claude/err-questhelper-current-a…
chsami Oct 14, 2026
9a25c78
fix(gameobject): bound scene object scan by the worldview tile array
chsami Oct 14, 2026
b08ec99
Merge pull request #1961 from chsami/claude/err-scene-objects-small-w…
chsami Oct 14, 2026
f3419fb
fix(agentserver): treat failed response body writes as client disconn…
chsami Oct 14, 2026
83207f2
Merge pull request #1962 from chsami/claude/err-agentserver-windows-d…
chsami Oct 14, 2026
737e486
fix(config-panel): stop inventory setups combo box crashing on the EDT
chsami Oct 14, 2026
6ff6307
Merge pull request #1963 from chsami/claude/err-config-panel-inventor…
chsami Oct 14, 2026
81c4a51
fix(config): detect InventorySetup config items by return type on reset
chsami Oct 14, 2026
cec285b
Merge pull request #1964 from chsami/claude/err-config-reset-inventor…
chsami Oct 14, 2026
b100688
fix(ge): return null from GrandExchangeWidget.getSlot for a null slot
chsami Oct 14, 2026
5263c72
fix(player): guard null LocalPoint in instanced getWorldLocation_Inte…
chsami Oct 14, 2026
6770c7d
docs(movement): do not claim gotcha 26 fallback handles boat worldviews
chsami Oct 14, 2026
237b180
Merge pull request #1966 from chsami/claude/err-rs2player-instance-lo…
chsami Oct 14, 2026
a59bdd5
fix(agentserver): stop quest helper on the client thread
chsami Oct 14, 2026
a8a6657
Merge pull request #1967 from chsami/claude/err-questhelper-agentserv…
chsami Oct 14, 2026
4f1d8ed
fix(shop): guard Rs2Shop.itemBounds against missing or unpopulated sh…
chsami Oct 14, 2026
b0a2838
Merge pull request #1968 from chsami/claude/err-rs2shop-itembounds-npe
chsami Oct 14, 2026
cdd2073
fix(npc): read Rs2NpcModel id on the client thread
chsami Oct 14, 2026
4a0e2d9
Merge pull request #1969 from chsami/claude/err-rs2npcmodel-getid-off…
chsami Oct 14, 2026
0583bdd
Merge pull request #1965 from chsami/claude/err-ge-buy-no-free-slot-npe
chsami Oct 14, 2026
eca9e7b
fix(grounditems): hold table lock while copying collected ground items
chsami Oct 14, 2026
1ccb685
Merge pull request #1970 from chsami/claude/err-grounditems-synchroni…
chsami Oct 14, 2026
08982ce
fix(tileobject): fall back to base composition when impostor is unres…
chsami Oct 14, 2026
e7a9f3b
fix(tileobject): refuse to click multiloc objects whose impostor is h…
chsami Oct 14, 2026
77d8830
Merge pull request #1971 from chsami/claude/err-tileobject-click-null…
chsami Oct 14, 2026
c0f02ac
fix(ge): return null from getConfirm when the offer container is closed
chsami Oct 14, 2026
e4a2604
fix(ge): fail processOffer when the confirm button cannot be clicked
chsami Oct 14, 2026
5ebae35
Merge pull request #1972 from chsami/claude/err-ge-getconfirm-null-of…
chsami Oct 14, 2026
c927691
fix(config-panel): guard null config descriptor and plugin in Microbo…
chsami Oct 15, 2026
15c75cc
Merge pull request #1973 from chsami/claude/err-config-panel-null-des…
chsami Oct 15, 2026
1d08c63
fix(shortestpath): ignore null walk targets from the panel Start buttons
chsami Oct 15, 2026
5455d91
Merge pull request #1974 from chsami/claude/err-shortestpath-panel-st…
chsami Oct 15, 2026
a7f6f7e
fix(bank): guard Rs2Bank scroll helpers against unloaded bank widgets
chsami Oct 15, 2026
275b156
Merge pull request #1975 from chsami/claude/err-rs2bank-scroll-helper…
chsami Oct 15, 2026
30ffaf1
fix(api): build NPC and player cache snapshots on the client thread
chsami Oct 15, 2026
12a10bd
Merge pull request #1976 from chsami/claude/err-rs2npccache-getstream…
chsami Oct 15, 2026
9ccbac7
fix(camera): skip projection for LocalPoints outside their worldview …
chsami Oct 15, 2026
7595378
docs(movement): null-check LocalPoint in gotcha 27 example
chsami Oct 15, 2026
a413638
Merge pull request #1977 from chsami/claude/err-rs2camera-istileonscr…
chsami Oct 15, 2026
c9cc693
fix(agentserver): treat empty JSON body as {} and malformed JSON as 400
chsami Oct 15, 2026
58215e3
Merge pull request #1978 from chsami/claude/err-agentserver-null-or-m…
chsami Oct 15, 2026
d2826eb
fix(questhelper): initialise requirements before collecting item requ…
chsami Oct 15, 2026
7fd14ff
Merge pull request #1979 from chsami/claude/err-questhelper-barcrawl-…
chsami Oct 15, 2026
0d9fd52
fix(questhelper): guard QuestOverviewPanel.addQuest against a null cu…
chsami Oct 15, 2026
16d9c46
Merge pull request #1980 from chsami/claude/err-questhelper-overview-…
chsami Oct 15, 2026
9837c97
fix(config): serialize in-JVM profile locks to avoid OverlappingFileL…
chsami Oct 15, 2026
1c7b220
Merge pull request #1982 from chsami/claude/err-profile-panel-overlap…
chsami Oct 15, 2026
ad50ebe
fix(bank): guard depositLootingBag wait against unloaded looting bag …
chsami Oct 15, 2026
be21695
Merge pull request #1983 from chsami/claude/err-rs2bank-deposit-looti…
chsami Oct 15, 2026
618184a
fix(questhelper): reset Shadow of the Storm incantation on chant
chsami Oct 15, 2026
f0b319b
Merge pull request #1984 from chsami/claude/err-questhelper-incantati…
chsami Oct 15, 2026
b73d8c4
fix(questhelper): refuse agent quest start before helpers are loaded
chsami Oct 15, 2026
03358bd
Merge pull request #1985 from chsami/claude/err-questhelper-agent-sta…
chsami Oct 15, 2026
cccc1c6
fix(discord): handle null player name in ban/break notifications
chsami Oct 15, 2026
753b67e
Merge pull request #1986 from chsami/claude/err-autologin-ban-discord…
chsami Oct 15, 2026
04e40d9
fix(combat): guard null style label widget in setAttackStyle
chsami Oct 15, 2026
9d1afd4
Merge pull request #1987 from chsami/claude/err-rs2combat-setattackst…
chsami Oct 15, 2026
9498fb1
fix(cannon): guard Rs2Cannon refill and repair against unknown cannon…
chsami Oct 15, 2026
ed5b058
fix(cannon): resolve owned cannon from OWNEDMCANNON varp in Rs2Cannon
chsami Oct 15, 2026
3ddab8c
Merge pull request #1988 from chsami/claude/err-rs2cannon-refill-null…
chsami Oct 15, 2026
51df0b4
fix(slayer): return null from getSlayerTask when DB field lookup is e…
chsami Oct 15, 2026
c35489e
Merge pull request #1989 from chsami/claude/err-rs2slayer-gettask-emp…
chsami Oct 15, 2026
19b5c33
fix(coords): treat empty path as unreachable in Rs2WorldPoint.distanc…
chsami Oct 15, 2026
56d7688
Merge pull request #1991 from chsami/claude/err-rs2worldpoint-distanc…
chsami Oct 15, 2026
7a99227
fix(camera): keep angleToTile null-safe without a local player
chsami Oct 15, 2026
3921083
Merge pull request #1992 from chsami/claude/err-rs2camera-angletotile…
chsami Oct 15, 2026
fb26b19
fix(blocking-events): attach throwable when logging BlockingEvent fai…
chsami Oct 15, 2026
32e5025
Merge pull request #1993 from chsami/claude/err-blockingevent-manager…
chsami Oct 15, 2026
1f92114
fix(npc): keep Rs2NpcModel distance helpers null-safe without a local…
chsami Oct 16, 2026
6970956
Merge pull request #1994 from chsami/claude/err-rs2npcmodel-distance-…
chsami Oct 16, 2026
024a1da
fix(gameobject): return false from clickObject when player or object …
chsami Oct 16, 2026
0e0ee7d
Merge pull request #1995 from chsami/claude/err-rs2gameobject-clickob…
chsami Oct 16, 2026
4e5a032
fix(bank): return false from walkToBank when no bank location is known
chsami Oct 16, 2026
e58b181
docs(bank): note null bankLocation precedence in walkToBank javadoc
chsami Oct 16, 2026
ce97fe6
Merge pull request #1996 from chsami/claude/err-rs2bank-walktobank-nu…
chsami Oct 16, 2026
71e47ac
fix(bank): drop Client of Kourend gate from Kourend banks and deposit…
chsami Oct 16, 2026
4983c3e
Merge pull request #1997 from chsami/claude/err-thievingstalls-hosidi…
chsami Oct 16, 2026
9503aec
fix(agentserver): reject wrong-typed JSON body fields with 400 instea…
chsami Oct 16, 2026
1d90449
fix(agentserver): use typed body accessors in remaining handlers and …
chsami Oct 16, 2026
69a5f8f
Merge pull request #1998 from chsami/claude/err-agentserver-json-fiel…
chsami Oct 16, 2026
874bf7f
fix(shortestpath): resolve clue location on the client thread
chsami Oct 17, 2026
63bf876
Merge pull request #1999 from chsami/claude/err-shortestpath-clue-pan…
chsami Oct 17, 2026
2348cc7
fix(inventory): return null instead of NPE when no widget matches ite…
chsami Oct 17, 2026
fc0b9dc
Merge pull request #2000 from chsami/claude/err-rs2inventory-invokeme…
chsami Oct 17, 2026
7e08ad8
chore: merge main (2.6.33) back into development
chsami Oct 18, 2026
16adb38
Merge pull request #2001 from chsami/claude/backmerge-main-2.6.33
chsami Oct 18, 2026
d727b59
Merge branch 'development' into claude/release-microbot-2.6.34
chsami Oct 18, 2026
0510c9f
chore: bump Microbot to 2.6.34
chsami Oct 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/entity-guides/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ Route camera turns only for unreachable targets: [movement gotcha 23](movement.m

Camera turns wait for the yaw to settle before returning: [movement gotcha 24](movement.md#24-a-camera-turn-is-finished-only-when-the-yaw-stops-changing).

Scene tile loops on a boat worldview must use the tile array bounds: [movement gotcha 25](movement.md#25-bound-scene-tile-loops-by-the-tile-array-not-constantsscene_size).

Instance location conversions must null-check `LocalPoint.fromWorld`: [movement gotcha 26](movement.md#26-null-check-localpointfromworld-before-converting-instance-coordinates).

Bounds-check a `LocalPoint` against its worldview before projecting it to the canvas: [movement gotcha 27](movement.md#27-bounds-check-a-localpoint-against-its-own-worldview-before-projecting-it).

## Format

Each entity guide is a numbered list of gotchas. Each entry follows this structure:
Expand Down
14 changes: 14 additions & 0 deletions docs/entity-guides/items.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,20 @@ An ordinary logout can be a break or world hop, so it must keep normal AutoLogin
**Defensive check:** Run `AutoLoginSuppressionTest`, `AutoLoginPluginSupplyStopTest`, `ScriptStartedTest`, `TerminalSupplyLogoutTest`, and `InventorySetupSupplyLogoutTest`. Verify live that a shortage stays logged out and restarting a gameplay script permits login.


## 16. Bank widgets can be missing or blank right after the bank opens

`BANK_ITEM_CONTAINER` can be null and the `BANK_ITEM_COUNT_TOP` text can be `""` while the bank interface is opening, closing or rebuilding. Never `assert` a widget non-null (asserts are disabled at runtime) and never `Integer.parseInt` widget text without handling blank or non-numeric input. `Rs2Bank.getBankItemCount()` returns 0 for unpopulated text, and `scrollBankToSlot` returns false before computing a scroll when the container is missing. The looting bag view (`15:11`) can be unloaded or have null/empty children once the bag closes, so `depositLootingBag` waits on `Rs2Bank.isLootingBagViewEmpty()`, which reads the widget on the client thread and treats a missing widget or children as emptied.

**Defensive check:** Run `Rs2BankUnloadedWidgetsTest` and `Rs2BankLootingBagViewTest`.


## 17. An inventory container may have no child for the item's slot

While a shop, bank, deposit box or Grand Exchange inventory is open or rebuilding, the active container's children may not include a widget whose index matches `Rs2ItemModel.getSlot()`. Look the child up with `findFirst().orElse(null)` and fall back to `rs2Item.getInventoryActions()`; never pass `null` to `Optional.orElseGet`, which throws an NPE instead of returning null.

**Defensive check:** Run `Rs2InventoryInvokeMenuTest`.


## Terminal supply stops need observed logout

An owning script can set `withMissingSupplyHandler` on its Inventory Setup loader to keep its scheduled loop alive while `TerminalLogoutPrompt` closes the bank and requests logout. Do not show a blocking shortage notification or cancel that loop until `LOGGED_OUT` is observed. The default loader policy stays unchanged when no handler is supplied; ordinary breaks and world hops do not arm terminal Auto Login suppression.
Expand Down
67 changes: 67 additions & 0 deletions docs/entity-guides/movement.md
Original file line number Diff line number Diff line change
Expand Up @@ -481,3 +481,70 @@ north and a pitch of 0 sends `adjustPitch` holding UP, so a fake 0 looked like a

**Defensive check:** `Rs2CameraSettleTest` simulates a camera that coasts after release. It
also covers the timeout bound and the client-thread no-op.

## 25. Bound scene tile loops by the tile array, not `Constants.SCENE_SIZE`

The local player's worldview is not always the 104x104 top-level scene. On a sailing boat
the player stands in a sub-worldview whose `Scene.getTiles()` is only 8 or 16 tiles wide.
A loop that runs `x, y < Constants.SCENE_SIZE` over that array throws
`ArrayIndexOutOfBoundsException` at the boat's width.

**Why this matters:** `Rs2GameObject.getSceneObjects` read tiles from
`player.getWorldView().getScene()` and looped to `SCENE_SIZE`. Every scene-object query made
from a boat threw `Index 8 out of bounds for length 8`, which aborted the walker inside
`handleNearbyRawPathSceneObjects` and crashed banking in scripts that called `findBank`.

**Pattern to follow:** take the loop bounds from the array you index (`tiles[z].length`,
`tiles[z][0].length`), or from `worldView.getSizeX()` / `getSizeY()`, whenever the tiles come
from a worldview that is not guaranteed to be the top-level one.

**Defensive check:** `Rs2GameObjectSmallWorldViewTest` queries wall objects on an 8x8
worldview and expects the object on the last tile.

## 26. Null-check `LocalPoint.fromWorld` before converting instance coordinates

`LocalPoint.fromWorld(worldView, worldPoint)` returns `null` when the plane differs from the world view's plane or the tile is outside that scene, for example in an instance while the scene is still loading. The fallback below only prevents the crash: if the player is on a boat sub-worldview, `player.getLocalLocation()` is in boat space and does not give the real sea position. `WorldPoint.fromLocalInstance` dereferences its `LocalPoint` immediately, so the result must be checked first.

**Why this matters:** `Rs2Player.getWorldLocation_Internal()` passed the `fromWorld` result straight into `fromLocalInstance`. Telemetry for 2.6.30 and 2.6.31 recorded about 585 NullPointerExceptions, most from `KourendLibraryOverlay.render` calling `Rs2Player.getWorldLocation()` every frame.

**Pattern to follow:**

```java
// Wrong: NPE when the world point does not resolve in the top-level scene.
LocalPoint l = LocalPoint.fromWorld(client.getTopLevelWorldView(), player.getWorldLocation());
return WorldPoint.fromLocalInstance(client, l);

// Right: fall back to the player's own local point, then to the raw world point.
LocalPoint l = LocalPoint.fromWorld(client.getTopLevelWorldView(), player.getWorldLocation());
if (l == null) l = player.getLocalLocation();
if (l == null) return player.getWorldLocation();
return WorldPoint.fromLocalInstance(client, l);
```

**Where this applies:** `Rs2Player.getWorldLocation_Internal()` and any helper that converts an actor's world point to an instance template point.

**Defensive check:** Mock a world view whose plane differs from the player's world point and assert the helper returns a fallback point, as in `Rs2PlayerInstanceWorldLocationTest`.

## 27. Bounds-check a `LocalPoint` against its own worldview before projecting it

`Perspective.getCanvasTilePoly` only rejects points outside the extended scene (`getSizeX() + 2 * ESCENE_OFFSET` on the top-level view). A tile with scene X/Y in 104..143, or a negative one, still reaches `WorldView.getTileHeight`, which indexes the normal scene height array and throws `ArrayIndexOutOfBoundsException`. Resolve the worldview from `localPoint.getWorldView()` and check the scene X/Y against `0..getSizeX()-1` / `0..getSizeY()-1` before projecting.

**Why this matters:** `Rs2Camera.isTileOnScreen` passed NPC and tile-object local points straight to `getCanvasTilePoly`. Telemetry for 2.6.30 to 2.6.32 recorded `Index 109/111/126 out of bounds for length 105` from `Rs2NpcModel.click` and `Rs2TileObjectModel.click`, which aborted the interaction instead of turning the camera.

**Pattern to follow:**

```java
// Wrong: out-of-scene points throw inside Perspective.
Polygon poly = Perspective.getCanvasTilePoly(client, localPoint);

// Right: treat a point outside its worldview's scene as off screen.
if (localPoint == null) return false;
WorldView wv = client.getWorldView(localPoint.getWorldView());
if (wv == null || localPoint.getSceneX() < 0 || localPoint.getSceneY() < 0
|| localPoint.getSceneX() >= wv.getSizeX() || localPoint.getSceneY() >= wv.getSizeY()) return false;
Polygon poly = Perspective.getCanvasTilePoly(client, localPoint);
```

**Where this applies:** `Rs2Camera.isTileOnScreen(LocalPoint)`, `Rs2Camera.isTileOnScreen(TileObject)`, and any helper that projects an entity's local point to the canvas.

**Defensive check:** Mock a worldview whose `getTileHeight` throws and assert out-of-scene points return false without calling it, as in `Rs2CameraTileOnScreenTest`.
6 changes: 3 additions & 3 deletions docs/entity-guides/worlds.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ return Microbot.getClientThread().runOnClientThreadOptional(() -> {
}).orElse(-1);
```

**Where this applies:** `Rs2Player` accessors (`getWorldLocation_Internal`, `getPoseAnimation`, `getAnimation`, `getCombatLevel`, `getLocalPlayer`, `getLocalLocation`, `getGraphicId`, `hasSpotAnimation`), the position accessors of `Rs2ActorModel` (`getWorldView`, `getWorldLocation`, `getLocalLocation`, `projectActorLocationToMainWorld`) and its `getAnimation` (returns -1), which `new Rs2PlayerModel()` reaches with a null actor, and any new helper that reads the local player.
**Where this applies:** `Rs2Player` accessors (`getWorldLocation_Internal`, `getPoseAnimation`, `getAnimation`, `getCombatLevel`, `getLocalPlayer`, `getLocalLocation`, `getGraphicId`, `hasSpotAnimation`), the position accessors of `Rs2ActorModel` (`getWorldView`, `getWorldLocation`, `getLocalLocation`, `projectActorLocationToMainWorld`) and its `getAnimation` (returns -1), which `new Rs2PlayerModel()` reaches with a null actor, the callers of those accessors (`Rs2Camera.angleToTile` returns the current facing so a turn is a no-op), both `Rs2NpcModel` classes' `getDistanceFromPlayer` (returns `Integer.MAX_VALUE`) and `isWithinDistanceFromPlayer` (returns `false`), `Rs2GameObject.clickObject` (returns `false` when it falls back to world-point distance), and any new helper that reads the local player.

**Defensive check:** Mock `Client.getLocalPlayer()` to return `null` and assert each accessor returns its fallback, as in `Rs2PlayerNullLocalPlayerTest` and `Rs2PlayerModelNullLocalPlayerTest`.
**Defensive check:** Mock `Client.getLocalPlayer()` to return `null` and assert each accessor returns its fallback, as in `Rs2PlayerNullLocalPlayerTest`, `Rs2PlayerModelNullLocalPlayerTest` and `Rs2NpcModelDistanceFromPlayerTest`.

**Wrapped locations:** `Rs2Player.getRs2WorldPoint()` still returns an `Rs2WorldPoint`, but it can wrap a `null` `WorldPoint` in these states. `pathTo` returns `null` and `distanceToPath` returns `Integer.MAX_VALUE` for it; check `getWorldPoint() != null` before calling `getX()`, `getY()` or `getPlane()`, as in `Rs2WorldPointNullPointTest`.

**Location consumers:** `Rs2Player.getWorldLocation()` itself returns `null` in these states. Guard it before passing it to `Rs2RouteRequest.to`/`toAny` (which reject a null start) or `WorldPoint.distanceTo`. `Rs2Bank.getNearestBank`, `getNearestBankRoute` and `getPathToNearestBank` return no bank, `isNearBank` returns `false`, and `Rs2Walker.getDistanceBetween` returns `Integer.MAX_VALUE` for a null endpoint, as in `Rs2BankNullPlayerLocationTest`.
**Location consumers:** `Rs2Player.getWorldLocation()` itself returns `null` in these states. Guard it before passing it to `Rs2RouteRequest.to`/`toAny` (which reject a null start) or `WorldPoint.distanceTo`. `Rs2Bank.getNearestBank`, `getNearestBankRoute` and `getPathToNearestBank` return no bank, `isNearBank` and `walkToBank` return `false`, `BankLocation.MINING_GUILD.hasRequirements` returns `false`, and `Rs2Walker.getDistanceBetween` returns `Integer.MAX_VALUE` for a null endpoint, as in `Rs2BankNullPlayerLocationTest`.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ project.build.version=1.13.2
runelite.injected-client.version=1.13.2

glslang.path=
microbot.version=2.6.33
microbot.version=2.6.34
microbot.commit.sha=nogit
microbot.repo.url=http://138.201.81.246:8081/repository/microbot-snapshot/
microbot.repo.username=
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import com.google.common.base.Strings;
import com.google.common.collect.ComparisonChain;
import com.google.gson.Gson;
import com.google.gson.JsonParseException;
import lombok.Getter;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
Expand Down Expand Up @@ -1259,7 +1260,7 @@ public <T extends Config> void setDefaultConfiguration(T proxy, boolean override
// null and the empty string are treated identically in sendConfig and treated as an unset
// If a config value defaults to "" and the current value is null, it will cause an extra
// unset to be sent, so treat them as equal
boolean isInventorySetup = item.keyName().toLowerCase().contains("inventorysetup");
boolean isInventorySetup = method.getReturnType() == InventorySetup.class;
if (Objects.equals(current, valueString) || (Strings.isNullOrEmpty(current) && Strings.isNullOrEmpty(valueString)))
{
if (isInventorySetup) {
Expand All @@ -1274,9 +1275,16 @@ public <T extends Config> void setDefaultConfiguration(T proxy, boolean override
if (current != null && !current.isBlank()) {
if (isInventorySetup) {
var setups = configInventorySetupDataManager.loadV3Setups(this);
InventorySetup currentObj = gson.fromJson(current, InventorySetup.class);
InventorySetup currentObj;
try {
currentObj = gson.fromJson(current, InventorySetup.class);
} catch (JsonParseException | IllegalStateException e) {
log.debug("Unable to parse inventory setup for {}.{}", group.value(), item.keyName(), e);
currentObj = null;
}
String currentName = currentObj == null ? null : currentObj.getName();
if (!setups.isEmpty() && setups.stream()
.noneMatch(x -> x.getName().equalsIgnoreCase(currentObj.getName()))) {
.noneMatch(x -> x.getName().equalsIgnoreCase(currentName))) {
setConfiguration(group.value(), item.keyName(), "");
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Predicate;
import javax.inject.Inject;
import javax.inject.Singleton;
Expand All @@ -57,6 +58,7 @@
public class ProfileManager {
private static final File PROFILES_DIR = new File(RuneLite.RUNELITE_DIR, "microbot-profiles");
private static final File PROFILES = new File(PROFILES_DIR, "profiles.json");
private static final ReentrantLock PROCESS_LOCK = new ReentrantLock();

private final Gson gson;

Expand All @@ -73,11 +75,26 @@ public class Lock implements AutoCloseable {

@SneakyThrows
public Lock() {
lockFile = new File(PROFILES_DIR, "profiles.lck");
lockOut = new FileOutputStream(lockFile);
lockChannel = lockOut.getChannel();
lockChannel.lock();
profiles = new ArrayList<>(load());
PROCESS_LOCK.lock();
FileOutputStream out = null;
try {
lockFile = new File(PROFILES_DIR, "profiles.lck");
out = new FileOutputStream(lockFile);
out.getChannel().lock();
lockOut = out;
lockChannel = out.getChannel();
profiles = new ArrayList<>(load());
} catch (Throwable t) {
if (out != null) {
try {
out.close();
} catch (IOException e) {
t.addSuppressed(e);
}
}
PROCESS_LOCK.unlock();
throw t;
}
}

private List<ConfigProfile> load() {
Expand All @@ -97,6 +114,19 @@ private List<ConfigProfile> load() {
@Override
@SneakyThrows
public void close() {
try {
save();
} finally {
try {
lockOut.close();
lockFile.delete();
} finally {
PROCESS_LOCK.unlock();
}
}
}

private void save() throws IOException {
if (modified) {
log.debug("saving {} profiles", profiles.size());

Expand All @@ -119,9 +149,6 @@ public void close() {
Files.move(tempFile.toPath(), PROFILES.toPath(), StandardCopyOption.REPLACE_EXISTING);
}
}

lockOut.close();
lockFile.delete();
}

public List<ConfigProfile> getProfiles() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,10 @@ static class PriceHighlight
private final Map<WorldPoint, Lootbeam> lootbeams = new HashMap<>();

public static Table<WorldPoint, Integer, GroundItem> getCollectedGroundItems() {
return ImmutableTable.copyOf(collectedGroundItems);
synchronized (collectedGroundItems)
{
return ImmutableTable.copyOf(collectedGroundItems);
}
}

@Provides
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ private void loopOnce() {
try {
validateAndEnqueueWithBackoff();
} catch (Throwable t) {
Microbot.log(Level.ERROR, "BlockingEvent loop error: %s", t);
log.error("BlockingEvent loop error", t);
} finally {
// re-schedule using the latest currentDelay (volatile)
loopFuture = scheduler.schedule(this::loopOnce, currentDelay, TimeUnit.MILLISECONDS);
Expand All @@ -138,10 +138,7 @@ private void validateAndEnqueueWithBackoff() {
}
}
} catch (Exception ex) {
Microbot.log(Level.ERROR,
"Error validating BlockingEvent (%s): %s",
event.getName(),
ex);
Microbot.log("Error validating BlockingEvent (" + event.getName() + ")", Level.ERROR, ex);
}
}
}
Expand Down Expand Up @@ -194,10 +191,7 @@ public boolean shouldBlockAndProcess()
}
catch (Exception ex)
{
Microbot.log(Level.ERROR,
"Error executing BlockingEvent (%s): %s",
event.getName(),
ex);
Microbot.log("Error executing BlockingEvent (" + event.getName() + ")", Level.ERROR, ex);
}
finally
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -480,8 +480,9 @@ private void updatePlayerNameCache() {
// detect fresh login - update player name cache
if (currentlyLoggedIn && !wasLoggedIn) {
Rs2PlayerModel localPlayer = Rs2Player.getLocalPlayer();
if (localPlayer != null && localPlayer.getName() != null) {
lastKnownPlayerName = localPlayer.getName();
String playerName = localPlayer != null ? localPlayer.getName() : null;
if (playerName != null) {
lastKnownPlayerName = playerName;
log.info("Updated cached player name: {}", lastKnownPlayerName);
}
}
Expand Down
Loading
Loading