Skip to content

fix: chunk load before chest placement + idempotent destroyChest - #102

Open
wangzhizhou wants to merge 4 commits into
DevCyntrix:mainfrom
OrzMC:main
Open

fix: chunk load before chest placement + idempotent destroyChest#102
wangzhizhou wants to merge 4 commits into
DevCyntrix:mainfrom
OrzMC:main

Conversation

@wangzhizhou

Copy link
Copy Markdown

Bug Description

DeathChest 3.0.1 has two related bugs:

  1. Items lost when a player dies and disconnects immediately — the death chest block placement is deferred to the next tick via runTask, and if the player disconnects right after death, the chunk may unload before the chest is placed, silently discarding the block placement while the drops have already been cleared. Items are permanently lost.

  2. IllegalArgumentException: Invalid model during server startup — when expired chests are loaded at startup, the expiration task can race with the model being added to the loaded cache, causing loadedChests.remove() to return null and throw.

Root Cause

  1. BlockCreationChestListener.onCreate uses BukkitRunnable().runTask(plugin) to defer location.getBlock().setType(CHEST) to the next tick (comment: to prevent chests being destroyed by bed explosions in the nether). If the chunk unloads before that tick runs (player death + immediate disconnect), setType writes into an unloaded chunk and is silently discarded — but event.getDrops().clear() already ran synchronously.

  2. DeathChestService.destroyChest calls loadedChests.remove(...) and throws IllegalArgumentException("Invalid model") if the result is null. During startup, ExpirationRunnable can run before the model is added to the loaded cache.

Fix

  1. BlockCreationChestListener: before placing the chest block, check if the chunk is loaded; if not, force-load it synchronously. Retry every tick (up to 40 ticks / 2s) if still not loaded, then place the chest and log a confirmation.

  2. DeathChestService.destroyChest: make destruction idempotent — if the model is already removed from the cache, return silently instead of throwing.

How to Verify

  1. Kill a player and disconnect within 2 seconds, then check for a chest at the death location — the chest must exist with all items.
  2. Restart the server with expired chests present — no Invalid model exception in the log.

Test Plan

  • Regression test: death + immediate disconnect → chest placed with items (14/14 rounds passed on test server)
  • Restart with expired chests → no exception (verified on test server)
  • Manual verification on live server (all 3 servers deployed with the fixed jar)

Risk Assessment

Low — the first fix only adds chunk-loading before placement (retry logic bounded at 2s); the second fix changes an exception into a silent return for an already-idempotent operation.

OrzMC added 2 commits August 5, 2026 08:19
Death chest block was placed on a possibly unloaded chunk when the player
disconnected right after dying, silently discarding the block change and
causing items (already cleared from the world) to be lost forever.

Now we force-load the chunk (with retry up to 40 ticks) before setType(CHEST).
During server startup, expired chests are loaded and their expiration
tasks run before (or racing with) the model being added to the loaded
cache, causing loadedChests.remove() to return null and throwing
IllegalArgumentException 'Invalid model'.

Now we return silently instead - destroying a chest is idempotent.
BreakAnimationRunnable.process can exceed 1.0 (async animation runs on real
time while destruction is tick-scheduled; at low TPS the 600s chest takes
~1600s to destroy) and PaperBreakAnimation.state/9f accepts unclamped input,
producing IllegalArgumentException: progress must be between 0.0 and 1.0
every second -> thread pool flood -> player timeouts (MCSM 2026-08-12 event).

Double clamp: caller clamps process, API clamps progress (defense in depth).
Reproduced locally: old jar 266 exceptions, fixed jar 0.
fix: clamp block break animation progress to [0,1]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant