IntellectualSites / IntellectualSites/PlotSquared
Biome writes at y = world max (320) → ArrayIndexOutOfBoundsException on Paper 26.2 in /plot setbiome and plot clear
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 653
- Forks
- 975
- Avg merge
- 2m
- Merged PRs (30d)
- 6
Description
Server Implementation
Paper
Server Version
Paper 26.2 (build 121, 26.2-121-main@a2a42c5; also reproduced on build 112). WorldEdit 7.4.5 (7.4.5+7590-b8dc4c1).
Describe the bug
Since moving the server from Paper 1.21.11 to Paper 26.2, every /plot setbiome <biome> and every plot clear (/plot delete → /plot confirm) prints one ArrayIndexOutOfBoundsException: Index 24 out of bounds for length 24 per chunk of the plot. The world is a standard -64..319 world (24 sections).
The throw site is WorldEdit's adapter-26.2 PaperweightAdapter.setBiome, but the cause is PlotSquared asking it to set a biome at y = 320, one block above the world. Two independent code paths do this:
1. /plot setbiome — Plot.getRegions() uses the exclusive max build height as an inclusive region bound.
if (!this.isMerged()) {
Location pos1 = this.getBottomAbs().withY(getArea().getMinBuildHeight());
Location pos2 = this.getTopAbs().withY(getArea().getMaxBuildHeight()); // exclusive (default versionMaxHeight() + 1 == 320)
CuboidRegion rg = new CuboidRegion(pos1.getBlockVector3(), pos2.getBlockVector3());
PlotArea.getMaxBuildHeight() is documented "Exclusive" and defaults to versionMaxHeight() + 1 (320). The merged-plot branch of the same method already subtracts 1 (int maxHeight = getArea().getMaxBuildHeight() - 1;, line 2563); the unmerged branch does not. RegionManager.setBiome → WorldUtil.setBiomes then does region.forEach(bv -> world.setBiome(bv, biome)), and CuboidRegion iteration is inclusive, so the last layer visited is y = 320.
2. Plot clear — BukkitQueueCoordinator.enqueue rebuilds biome Y from the layer index without the negative-section offset.
// blocks (line 180) — correct
int y = ChunkUtil.getY(layer + localChunk.getMinSection(), j);
...
// biomes (line 201) — missing the offset
int y = ChunkUtil.getY(layer, j);
LocalChunk.setBiome stores biomes with getLayerIndex(y) = (y >> 4) - minSection, exactly like blocks. Reading them back without adding minSection back shifts every biome up by 64 blocks in a -64..319 world: a biome queued for y = -64 is written at y = 0, and a biome queued for y ≥ 256 is written at y ≥ 320. HybridPlotManager.clearPlot queues setBiomeCuboid(minGenHeight .. maxGenHeight), so layer 20 (queued y = 256) is the first to land at y = 320 → index 24.
Why it only shows up on 26.2. WorldEdit's adapter-1.21.11 implemented setBiome via vanilla ChunkAccess.setBiome, which clamps Y into the world. The adapter-26.1/adapter-26.2 implementation does chunk.getSection(chunk.getSectionIndex(y)).getBiomes() directly, with no clamp. So both PlotSquared bugs are older than 26.2 and were previously silently swallowed by the clamp (which also means path 2 has been writing biomes 64 blocks too high, and never writing y < 0, in every extended-height world).
Since BukkitChunkCoordinator catches the throwable per chunk and continues, the visible effect is: /plot setbiome applies the biome to every valid layer but skips the refreshChunk for the chunk that threw (players see the old biome until the chunk reloads); a plot clear sets the biome for y ≥ 0 only and skips the tile-entity / entity restoration for that chunk.
To Reproduce
- Paper 26.2 + WorldEdit 7.4.5 + PlotSquared, plot world with default heights (
world.max_height: 320,world.max_gen_height: 319). - Stand in an unmerged plot, run
/plot setbiome flower_forest. - Console prints one
ArrayIndexOutOfBoundsException: Index 24 out of bounds for length 24per chunk. /plot delete+/plot confirmon the same plot prints the same trace per chunk (viaBukkitQueueCoordinator.lambda$enqueue$2).
Expected behaviour
No biome writes outside the world's Y range; the region top should be getMaxBuildHeight() - 1 like the merged branch, and the queue readback should be ChunkUtil.getY(layer + localChunk.getMinSection(), j) like the block loop directly above it.
Screenshots / Videos
No response
Error log (if applicable)
[WARN]: java.lang.ArrayIndexOutOfBoundsException: Index 24 out of bounds for length 24
[WARN]: at net.minecraft.world.level.chunk.ChunkAccess.getSection(ChunkAccess.java:231)
[WARN]: at WorldEdit.jar//com.sk89q.worldedit.bukkit.adapter.impl.v26_2.PaperweightAdapter.setBiome(PaperweightAdapter.java:445)
[WARN]: at WorldEdit.jar//com.sk89q.worldedit.bukkit.BukkitWorld.setBiome(BukkitWorld.java:589)
[WARN]: at PlotSquared.jar//com.plotsquared.core.util.WorldUtil.lambda$setBiomes$0(WorldUtil.java:230)
[WARN]: at java.base/java.lang.Iterable.forEach(Iterable.java:75)
[WARN]: at PlotSquared.jar//com.plotsquared.core.util.WorldUtil.setBiomes(WorldUtil.java:230)
[WARN]: at PlotSquared.jar//com.plotsquared.core.util.WorldUtil.setBiome(WorldUtil.java:83)
[WARN]: at PlotSquared.jar//com.plotsquared.core.util.RegionManager.lambda$setBiome$2(RegionManager.java:409)
[WARN]: at PlotSquared.jar//com.plotsquared.bukkit.queue.BukkitChunkCoordinator.run(BukkitChunkCoordinator.java:187)
[WARN]: at PlotSquared.jar//com.plotsquared.bukkit.util.task.BukkitPlotSquaredTask.runTask(BukkitPlotSquaredTask.java:39)
[WARN]: at PlotSquared.jar//com.plotsquared.core.util.task.PlotSquaredTask.run(PlotSquaredTask.java:44)
[WARN]: at org.bukkit.craftbukkit.scheduler.CraftTask.run(CraftTask.java:78)
[WARN]: at org.bukkit.craftbukkit.scheduler.CraftScheduler.mainThreadHeartbeat(CraftScheduler.java:474)
[WARN]: at net.minecraft.server.MinecraftServer.tickChildren(MinecraftServer.java:1767)
Plot clear variant:
[WARN]: java.lang.ArrayIndexOutOfBoundsException: Index 24 out of bounds for length 24
[WARN]: at net.minecraft.world.level.chunk.ChunkAccess.getSection(ChunkAccess.java:231)
[WARN]: at WorldEdit.jar//com.sk89q.worldedit.bukkit.adapter.impl.v26_2.PaperweightAdapter.setBiome(PaperweightAdapter.java:445)
[WARN]: at WorldEdit.jar//com.sk89q.worldedit.bukkit.BukkitWorld.setBiome(BukkitWorld.java:589)
[WARN]: at PlotSquared.jar//com.plotsquared.bukkit.queue.BukkitQueueCoordinator.lambda$enqueue$2(BukkitQueueCoordinator.java:203)
[WARN]: at PlotSquared.jar//com.plotsquared.bukkit.queue.BukkitChunkCoordinator.run(BukkitChunkCoordinator.java:187)
Plot Debugpaste
Not attached — this is a code-level report; both lines are unchanged on main (b49bd002) and nothing in the paste would add to it. Happy to provide one if you still want it.
PlotSquared Version
Observed on a 7.5.11-SNAPSHOT build; the offending lines are identical in 7.5.11 (where the line numbers in the trace match exactly), 7.6.0 and current main @ b49bd002.
Checklist
- I have included a Plot debugpaste. (see above)
- I am using the newest build from https://www.spigotmc.org/resources/77506/ and the issue still persists. (verified against the source of
main, see above)
Anything else?
Proposed fix (two one-line changes):
--- a/Core/src/main/java/com/plotsquared/core/plot/Plot.java
+++ b/Core/src/main/java/com/plotsquared/core/plot/Plot.java
@@ public @NonNull Set<CuboidRegion> getRegions() {
if (!this.isMerged()) {
Location pos1 = this.getBottomAbs().withY(getArea().getMinBuildHeight());
- Location pos2 = this.getTopAbs().withY(getArea().getMaxBuildHeight());
+ Location pos2 = this.getTopAbs().withY(getArea().getMaxBuildHeight() - 1);
--- a/Bukkit/src/main/java/com/plotsquared/bukkit/queue/BukkitQueueCoordinator.java
+++ b/Bukkit/src/main/java/com/plotsquared/bukkit/queue/BukkitQueueCoordinator.java
@@ for (int layer = 0; layer < localChunk.getBiomes().length; layer++) {
int x = sx + ChunkUtil.getX(j);
- int y = ChunkUtil.getY(layer, j);
+ int y = ChunkUtil.getY(layer + localChunk.getMinSection(), j);
int z = sz + ChunkUtil.getZ(j);
WorldEdit could additionally guard PaperweightAdapter.setBiome against out-of-range Y (as the 1.21.11 adapter effectively did), but the out-of-range coordinates originate here.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with Core/src/main/java/com/plotsquared/core/plot/Plot.java in getRegions() and Bukkit/src/main/java/com/plotsquared/bukkit/queue/BukkitQueueCoordinator.java in enqueue(), comparing the unmerged region bound and biome loop with the nearby merged and block paths. Apply the two coordinate corrections described in the report, then reproduce /plot setbiome and plot clear on an extended-height world. Done means no out-of-range biome writes or exceptions, including at negative and maximum Y layers.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100