Prototype and benchmark batched ChestShop hopper stock updates
Outcome
Restore hopper-driven ChestShop stock counters with bounded server overhead and immediate hopper-minecart protection. Build a prototype and measure it before recommending production enablement; performance is currently unmeasured.
Findings from source inspection
ItemMoveListener checks shop membership through nearby sign/block searches; updating a shop destination repeats sign discovery, parses the traded item and scans inventory.
StockCounterServiceImpl skips unchanged sign writes, but only after the lookup and counting work.
The transfer path refreshes one destination sign. Outgoing stock and additional signs are not refreshed by that path.
It predicts the resulting stock by adding the attempted transfer amount before completion. Failed insertion or later cancellation can leave an incorrect counter.
Separate immediate protection decisions from deferred stock refresh.
Maintain an in-memory container-to-shop index, including both halves of double chests and all associated signs. Cover startup, chunk load/unload, shop creation/removal, container topology changes and supported world-edit paths. Index readiness must not introduce a protection gap.
Keep transfer handling to cheap source/destination lookup and deduplicated dirty marking. Avoid inventory scans, item parsing, sign writes and database I/O in this path.
Drain dirty shops fairly in bounded batches on the server thread, reading actual inventory after transfers. Start with approximately one-second freshness as a tuning target, not a proven guarantee.
Refresh all associated signs only when their text changes. Retain pending work when the per-tick budget is exhausted; bound memory and define overload/reconciliation behavior without silently losing updates.
Never force-load chunks for refresh. Clean up pending state on unload/disable and rebuild it safely on load.
Keep finder/market database synchronization outside the transfer hot path; explicitly document whether this prototype updates sign counters only.
Verification and acceptance
Incoming and permitted outgoing transfers converge to actual stock, including multiple transfers within one refresh interval.
Failed/full-destination transfers and cancellations do not create phantom stock.
Minecart theft prevention remains immediate and works during index initialization/rebuild; preserve existing block-hopper protection semantics.
Sign removal, chunk unload/reload, shop creation and supported world edits cannot leave stale index entries or protection gaps.
Queue budget, deduplication, fairness and eventual refresh have meaningful regression coverage.
Run repeated, warmed comparisons of listener disabled, current listener and prototype on a disposable Paper server with a representative plugin stack.
Include large non-shop sorter networks, busy shop containers, full destinations and hopper minecarts. Record exact versions/configuration, fixture sizes, transfer rates, sample duration and run variance.
Report p95/p99 tick time, CPU/allocation cost, refresh latency and backlog growth. Set explicit acceptable overhead/freshness thresholds before judging the candidate; unit tests alone do not establish performance.
Verify effective ListenerControl registration and Paper hopper.disable-move-event settings in the intended deployment configuration.
Produce an evidence-backed enable/no-enable recommendation, configuration instructions and rollback procedure. Production enablement is a later deployment step.