From 67b9ea7a85ab2b0e02a1747b32055222507daed2 Mon Sep 17 00:00:00 2001 From: Reggie Reuss Date: Sat, 14 Mar 2026 18:12:34 -0400 Subject: [PATCH] Fix IndexOutOfBoundsException when slotTimers is empty or undersized If slotTimers is deserialized as an empty list rather than null (e.g. from a recovered or manually constructed JSON file), hydrateSlotTimers() skips initialization because it only checks for null. The three unguarded .get(slot) calls in screenOfferEvent() then throw IndexOutOfBoundsException on every GE offer event, silently dropping all real-time trades with no user-visible error. - AccountData.hydrateSlotTimers: reinitialize if size != 8, not just null - NewOfferEventPipelineHandler.screenOfferEvent: early return with log warning if slotActivityTimers is missing or undersized - FlippingPlugin.setWidgetsOnSlotTimers: skip loop if list undersized --- .../com/flippingutilities/controller/FlippingPlugin.java | 3 +++ .../controller/NewOfferEventPipelineHandler.java | 5 +++++ src/main/java/com/flippingutilities/model/AccountData.java | 2 +- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/flippingutilities/controller/FlippingPlugin.java b/src/main/java/com/flippingutilities/controller/FlippingPlugin.java index 24d19cc..055bef7 100644 --- a/src/main/java/com/flippingutilities/controller/FlippingPlugin.java +++ b/src/main/java/com/flippingutilities/controller/FlippingPlugin.java @@ -747,6 +747,9 @@ public void setWidgetsOnSlotTimers() { return; } + if (accountData.getSlotTimers().size() < 8) { + return; + } for (int slotIndex = 0; slotIndex < 8; slotIndex++) { SlotActivityTimer timer = accountData.getSlotTimers().get(slotIndex); if (timer == null) { diff --git a/src/main/java/com/flippingutilities/controller/NewOfferEventPipelineHandler.java b/src/main/java/com/flippingutilities/controller/NewOfferEventPipelineHandler.java index 5628e1d..334c5c2 100644 --- a/src/main/java/com/flippingutilities/controller/NewOfferEventPipelineHandler.java +++ b/src/main/java/com/flippingutilities/controller/NewOfferEventPipelineHandler.java @@ -117,6 +117,11 @@ public Optional screenOfferEvent(OfferEvent newOfferEvent) { Map lastOfferEventForEachSlot = plugin.getDataHandler().getAccountData(plugin.getCurrentlyLoggedInAccount()).getLastOffers(); List slotActivityTimers = plugin.getDataHandler().getAccountData(plugin.getCurrentlyLoggedInAccount()).getSlotTimers(); + if (slotActivityTimers == null || slotActivityTimers.size() < 8) { + log.warn("slotActivityTimers is missing or undersized ({}), skipping offer screening", + slotActivityTimers == null ? "null" : slotActivityTimers.size()); + return Optional.empty(); + } OfferEvent lastOfferEvent = lastOfferEventForEachSlot.get(newOfferEvent.getSlot()); //completely useless updates diff --git a/src/main/java/com/flippingutilities/model/AccountData.java b/src/main/java/com/flippingutilities/model/AccountData.java index 00965b7..ea6f92a 100644 --- a/src/main/java/com/flippingutilities/model/AccountData.java +++ b/src/main/java/com/flippingutilities/model/AccountData.java @@ -134,7 +134,7 @@ private void hydrateRecipeFlipGroups(FlippingPlugin plugin) { } private void hydrateSlotTimers(FlippingPlugin plugin) { - if (slotTimers == null) { + if (slotTimers == null || slotTimers.size() != 8) { slotTimers = setupSlotTimers(plugin); } else { slotTimers.forEach(timer -> {