perf+fix: parallel chunk fetches, lazy zero scan, fresh-island level=0#440
Merged
Merged
Conversation
A 1000-protection-range AcidIsland scan took 160s because loadChunks fetched 15,876 chunk positions sequentially via getChunkAtAsync, and each callback round-trip cost ~10ms even when the chunk was ungenerated and returned null. This change: - Always skip generation during scans (gen=false). Lazy zeroing via NewChunkListener already keeps the handicap aligned with chunks as they generate, so forcing the generator for every position in a large protection range is unnecessary and times out. - Fast-path ungenerated positions with World.isChunkGenerated, a synchronous region-file lookup that avoids the async-scheduler hop. - Issue up to 32 async chunk fetches in parallel per batch instead of recursing one-at-a-time. - Drop the raw Location.toString() from scan log lines in favour of "<world_name> x,y,z". - Distinguish the zero-scan completion log from regular scans. The /<gamemode> levelstatus command now reports, per island, the scan type (zero vs regular), world + xyz, elapsed time, and a monotonically increasing chunks-scanned/total counter. The level report adds a matching "Chunks scanned = N/total" line under the initial-count line. Locale keys added: admin.levelstatus.island-detail / island-queued / type-zero / type-regular, translated into all 16 supported locales. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
A 1000-protection-range new island reported level -44551 after a 1000-block flight. The handicap was 4,455,925 but the regular scan only found 784 points of value across the same chunks — a 5,683x over-counting. Two compounding causes: - Paper can fire ChunkLoadEvent with isNewChunk=true more than once for a given chunk under heavy chunk activity (ticket churn, parallel level-scan loads, plugin-triggered reloads). Each duplicate event credited the chunk to initialCount again. - The listener summed raw getValue per block while the regular scan applies per-material limits via limitCountAndValue. Limited high-value blocks could inflate the handicap past anything the scan would ever credit. Fix: - Track counted chunks per island in an in-memory Set<Long> keyed by packed (x,z). Skip if the chunk has already contributed during this server run. After a restart Paper reports isNewChunk=false for already- generated chunks, so prior-run chunks are not at risk on re-load. - Apply per-material limits in valueAt, capping each material at its configured limit within each chunk. This bounds the handicap to what the regular scan would credit. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The zero-island scan set initialCount via setInitialIslandCount(totalPoints), which wiped any listener credits captured during the scan. Chunks that generated mid-scan and were skipped by the chunk-poll (ungenerated at poll time) then appeared in the next level scan with no matching handicap, producing a stable positive level on a fresh island. Track per-island scan-visited chunks and listener-deferred credits so the post-scan drain folds in only the chunks the scan missed. Also stop the console spam from logging every pending zero-scan, only count actually-scanned chunks in the report's X/Y figure, and raise the default zero-scan-delay-ticks from 40 (2s) to 600 (30s) so underwater obsidian formation finishes before the listener snapshots. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
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.



Summary
getChunkAtAsyncround-trips for ungenerated chunks) to a few seconds. Two layered fixes: a syncWorld.isChunkGeneratedfast-path for chunks that have never existed, and a 32-way parallel batch for the chunks that have.gen=falsealways —NewChunkListeneralready keeps the initial-count handicap aligned with chunks as they generate during normal play, so the scan only ever needs to look at what's actually there.NewChunkListenersnapshots after a configurable delay and respects block-config limits, so paper ticket churn can't double-credit a chunk and cobblestone-heavy terrain can no longer inflate the handicap past anything the regular scan would credit./<gamemode> levelstatusnow reports useful diagnostics. Per island: scan type (zero/regular), world + xyz, elapsed time, and achunks scanned/totalcounter that now only counts chunks the scan actually read block data for. The level report adds a matchingChunks scanned = N/totalline.setInitialIslandCount(totalPoints)which wiped any listener credits captured during the scan. Chunks generating mid-scan that the chunk-poll had already skipped were then in the next level scan's total with no matching handicap, producing a stable positive level. Now tracks per-island scan-visited chunks and listener-deferred credits, then folds in only the chunks the scan missed during the post-scan drain.zero-scan-delay-ticksdefault from 40 (2s) to 600 (30s) so slow-forming terrain like underwater obsidian (lava sources flowing into adjacent water) finishes before the listener snapshots, instead of showing up later in a level scan with no matching handicap.Locale changes
Five new keys (
admin.levelstatus.island-detail,island-queued,type-zero,type-regular,pending-zeros) translated into all 16 supported locales.Test plan
mvn test— 229 tests pass, including new coverage:IslandActivitiesListenersTest— drain folds in deferred listener credits after zero scan; no-op when drain is empty.LevelsManagerTest—tryDeferZeroScanCredit/recordScanVisitedChunk/drainZeroScanDeferredcontract: visited chunks dropped, missed chunks summed and returned.IslandLevelCalculatorTidyUpTest— pins down the level-0 progress/interval cases.mvn clean package— clean build./aion a fresh island returns control immediately; console showsInitial zero scan complete for island at acidisland_world 0,60,0. Lazy zeroing will continue as new chunks generate./acid level <player>completes in well under the 5 min timeout (previously timed out)./acid levelstatusduring the scan shows increasing chunks-scanned count, world+xyz, elapsed time.Chunks scanned = N/Nat completion./acid level <player>— expect level 0 (was level 1)./acid level <player>again — expect level still 0 (was level 3 from missed underwater obsidian)./acid admin levelstatuswhile a scan is in flight — expectX/Yto show only generated-and-scanned chunks.🤖 Generated with Claude Code