From 368ca9c145c7a6948215e52a54b5d3f5f0c7a147 Mon Sep 17 00:00:00 2001 From: Pierpaolo Coletta Date: Sat, 30 Mar 2024 21:06:10 +0100 Subject: [PATCH] Fix creation of invalid block entity during world generation --- .../server/level/WorldGenRegion.java.patch | 49 ++++++++++++++----- .../world/level/chunk/LevelChunk.java.patch | 34 +++++++++---- 2 files changed, 63 insertions(+), 20 deletions(-) diff --git a/paper-server/patches/sources/net/minecraft/server/level/WorldGenRegion.java.patch b/paper-server/patches/sources/net/minecraft/server/level/WorldGenRegion.java.patch index 5f10cb020e..415887c78f 100644 --- a/paper-server/patches/sources/net/minecraft/server/level/WorldGenRegion.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/level/WorldGenRegion.java.patch @@ -1,12 +1,14 @@ --- a/net/minecraft/server/level/WorldGenRegion.java +++ b/net/minecraft/server/level/WorldGenRegion.java -@@ -169,7 +169,27 @@ - return k < this.generatingStep.directDependencies().size(); - } +@@ -167,7 +167,27 @@ + int k = this.center.getPos().getChessboardDistance(chunkX, chunkZ); + return k < this.generatingStep.directDependencies().size(); ++ } ++ + // Paper start - if loaded util + @Nullable - @Override ++ @Override + public ChunkAccess getChunkIfLoadedImmediately(int x, int z) { + return this.getChunk(x, z, ChunkStatus.FULL, false); + } @@ -21,13 +23,11 @@ + public final FluidState getFluidIfLoaded(BlockPos blockposition) { + ChunkAccess chunk = this.getChunkIfLoadedImmediately(blockposition.getX() >> 4, blockposition.getZ() >> 4); + return chunk == null ? null : chunk.getFluidState(blockposition); -+ } -+ // Paper end -+ -+ @Override - public BlockState getBlockState(BlockPos pos) { - return this.getChunk(SectionPos.blockToSectionCoord(pos.getX()), SectionPos.blockToSectionCoord(pos.getZ())).getBlockState(pos); } ++ // Paper end + + @Override + public BlockState getBlockState(BlockPos pos) { @@ -217,7 +237,8 @@ if (iblockdata.isAir()) { return false; @@ -62,7 +62,34 @@ return false; } } -@@ -336,6 +366,13 @@ +@@ -294,7 +324,7 @@ + return false; + } else { + ChunkAccess ichunkaccess = this.getChunk(pos); +- BlockState iblockdata1 = ichunkaccess.setBlockState(pos, state, false); ++ BlockState iblockdata1 = ichunkaccess.setBlockState(pos, state, false); final BlockState previousBlockState = iblockdata1; // Paper - Clear block entity before setting up a DUMMY block entity - obfhelper + + if (iblockdata1 != null) { + this.level.onBlockStateChange(pos, iblockdata1, state); +@@ -310,6 +340,17 @@ + ichunkaccess.removeBlockEntity(pos); + } + } else { ++ // Paper start - Clear block entity before setting up a DUMMY block entity ++ // The concept of removing a block entity when the block itself changes is generally lifted ++ // from LevelChunk#setBlockState. ++ // It is however to note that this may only run if the block actually changes. ++ // Otherwise a chest block entity generated by a structure template that is later "updated" to ++ // be waterlogged would remove its existing block entity (see PaperMC/Paper#10750) ++ // This logic is *also* found in LevelChunk#setBlockState. ++ if (previousBlockState != null && !java.util.Objects.equals(previousBlockState.getBlock(), state.getBlock())) { ++ ichunkaccess.removeBlockEntity(pos); ++ } ++ // Paper end - Clear block entity before setting up a DUMMY block entity + CompoundTag nbttagcompound = new CompoundTag(); + + nbttagcompound.putInt("x", pos.getX()); +@@ -336,6 +377,13 @@ @Override public boolean addFreshEntity(Entity entity) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/chunk/LevelChunk.java.patch b/paper-server/patches/sources/net/minecraft/world/level/chunk/LevelChunk.java.patch index 1cce60e598..32a19bf223 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/chunk/LevelChunk.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/chunk/LevelChunk.java.patch @@ -110,15 +110,15 @@ public FluidState getFluidState(int x, int y, int z) { - try { - int l = this.getSectionIndex(y); -- -- if (l >= 0 && l < this.sections.length) { -- LevelChunkSection chunksection = this.sections[l]; + // Paper start - Perf: Optimise Chunk#getFluid + // try { // Remove try catch + int index = this.getSectionIndex(y); + if (index >= 0 && index < this.sections.length) { + LevelChunkSection chunksection = this.sections[index]; +- if (l >= 0 && l < this.sections.length) { +- LevelChunkSection chunksection = this.sections[l]; +- if (!chunksection.hasOnlyAir()) { - return chunksection.getFluidState(x & 15, y & 15, z & 15); + return chunksection.states.get((y & 15) << 8 | (z & 15) << 4 | x & 15).getFluidState(); @@ -288,7 +288,7 @@ if (tileentity != null) { Level world = this.level; -@@ -549,9 +626,68 @@ +@@ -549,10 +626,69 @@ if (this.postLoad != null) { this.postLoad.run(this); this.postLoad = null; @@ -335,7 +335,7 @@ + } } + } -+ + + public void unloadCallback() { + org.bukkit.Server server = this.level.getCraftServer(); + org.bukkit.Chunk bukkitChunk = new org.bukkit.craftbukkit.CraftChunk(this); @@ -347,16 +347,17 @@ + // Paper start + this.loadedTicketLevel = false; + // Paper end -+ } + } + @Override + public boolean isUnsaved() { + return super.isUnsaved() && !this.mustNotSave; - } ++ } + // CraftBukkit end - ++ public boolean isEmpty() { return false; + } @@ -750,7 +886,7 @@ private void updateBlockEntityTicker(T blockEntity) { @@ -375,7 +376,22 @@ this.ticker = blockentityticker; } -@@ -867,11 +1003,13 @@ +@@ -860,18 +996,25 @@ + if (this.blockEntity.getType().isValid(iblockdata)) { + this.ticker.tick(LevelChunk.this.level, this.blockEntity.getBlockPos(), iblockdata, this.blockEntity); + this.loggedInvalidBlockState = false; +- } else if (!this.loggedInvalidBlockState) { +- this.loggedInvalidBlockState = true; +- LevelChunk.LOGGER.warn("Block entity {} @ {} state {} invalid for ticking:", new Object[]{LogUtils.defer(this::getType), LogUtils.defer(this::getPos), iblockdata}); ++ // Paper start - Remove the Block Entity if it's invalid ++ } else { ++ LevelChunk.this.removeBlockEntity(this.getPos()); ++ if (!this.loggedInvalidBlockState) { ++ this.loggedInvalidBlockState = true; ++ LevelChunk.LOGGER.warn("Block entity {} @ {} state {} invalid for ticking:", new Object[]{LogUtils.defer(this::getType), LogUtils.defer(this::getPos), iblockdata}); ++ } ++ // Paper end - Remove the Block Entity if it's invalid + } gameprofilerfiller.pop(); } catch (Throwable throwable) {