Retype bastions to match their world block - #31
Open
grepsedawk wants to merge 1 commit into
Open
Conversation
The legacy "old bastion" migration added the bastion_type column with DEFAULT 'citybastion', so every pre-existing bastion was recorded as a 50-radius city bastion while its world block stayed SPONGE. Those bastions enforce the wrong field and drop the wrong item when broken. The correct type can only be recovered from the block itself -- the lone type column in the table is the corrupted one -- and a block can only be read once its chunk is loaded. So a SQL migration can't fix this, and a proactive startup sweep would have to force-load the whole world and freeze the server. Instead, heal lazily: when a chunk loads, retype any bastion whose stored type no longer matches its block. Each block is read exactly when it is already in memory, the work spreads across normal play, and the correction persists across the daily restarts. This stays cheap on the hot path -- it only inspects bastions the index already knows are in the chunk, retypes in place (never structurally mutating the set the async erosion tasks iterate), and batches the DB writes off the main thread. Pin JDK 21 via mise so the build runs on the required toolchain.
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.
What
Heal bastions whose stored type no longer matches their world block.
Why
The legacy "old bastion" migration added the
bastion_typecolumn withDEFAULT 'citybastion', so every pre-existing bastion was recorded as a50-radius city bastion while its world block stayed
SPONGE. Those bastionsenforce the wrong field and drop the wrong item when broken.
The correct type can only be recovered from the block itself — the lone type
column in the table is the corrupted one — and a block can only be read once
its chunk is loaded. So a SQL migration can't fix this, and a proactive
startup sweep would have to force-load the whole world and freeze the server.
How
A
ChunkLoadEventlistener retypes any bastion whose stored type no longermatches its block, treating the block as the source of truth. The world heals
gradually as chunks load during normal play, and the correction persists
across restarts.
It stays cheap on the hot path:
read each — no block scanning);
the async erosion/regen tasks iterate (avoids
ConcurrentModificationException);Notes
mise.tomlso the build runs on the required toolchain.destroy-on-cache-miss bug) is intentionally out of scope here — it needs a
block sweep or a Citadel per-chunk reinforcement enumeration, which is a
cleaner follow-up.
Testing
./gradlew :plugins:bastion-paper:test— green (11 tests).