fix: chunk load before chest placement + idempotent destroyChest - #102
Open
wangzhizhou wants to merge 4 commits into
Open
fix: chunk load before chest placement + idempotent destroyChest#102wangzhizhou wants to merge 4 commits into
wangzhizhou wants to merge 4 commits into
Conversation
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]
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Bug Description
DeathChest 3.0.1 has two related bugs:
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.IllegalArgumentException: Invalid modelduring server startup — when expired chests are loaded at startup, the expiration task can race with the model being added to the loaded cache, causingloadedChests.remove()to returnnulland throw.Root Cause
BlockCreationChestListener.onCreateusesBukkitRunnable().runTask(plugin)to deferlocation.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),setTypewrites into an unloaded chunk and is silently discarded — butevent.getDrops().clear()already ran synchronously.DeathChestService.destroyChestcallsloadedChests.remove(...)and throwsIllegalArgumentException("Invalid model")if the result isnull. During startup,ExpirationRunnablecan run before the model is added to the loaded cache.Fix
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.DeathChestService.destroyChest: make destruction idempotent — if the model is already removed from the cache, return silently instead of throwing.How to Verify
Invalid modelexception in the log.Test Plan
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.