Co-authored-by: Bjarne Koll <git@lynxplay.dev>
Co-authored-by: Jake Potrebic <jake.m.potrebic@gmail.com>
Co-authored-by: Lulu13022002 <41980282+Lulu13022002@users.noreply.github.com>
Co-authored-by: MiniDigger | Martin <admin@minidigger.dev>
Co-authored-by: Nassim Jahnke <nassim@njahnke.dev>
Co-authored-by: Noah van der Aa <ndvdaa@gmail.com>
Co-authored-by: Owen1212055 <23108066+Owen1212055@users.noreply.github.com>
Co-authored-by: Shane Freeder <theboyetronic@gmail.com>
Co-authored-by: Spottedleaf <Spottedleaf@users.noreply.github.com>
Co-authored-by: Tamion <70228790+notTamion@users.noreply.github.com>
Co-authored-by: Warrior <50800980+Warriorrrr@users.noreply.github.com>
This commit is contained in:
Nassim Jahnke
2025-04-12 17:26:44 +02:00
parent 0767902699
commit f00727c57e
2092 changed files with 50551 additions and 48729 deletions

View File

@@ -39,15 +39,16 @@
}
private static void replaceMissingSections(Registry<Biome> biomeRegistry, LevelChunkSection[] sections) {
@@ -123,6 +_,7 @@
@@ -123,6 +_,8 @@
return GameEventListenerRegistry.NOOP;
}
+ public abstract BlockState getBlockState(final int x, final int y, final int z); // Paper
+
@Nullable
public abstract BlockState setBlockState(BlockPos pos, BlockState state, boolean isMoving);
@@ -273,6 +_,7 @@
public BlockState setBlockState(BlockPos pos, BlockState state) {
return this.setBlockState(pos, state, 3);
@@ -278,6 +_,7 @@
public boolean tryMarkSaved() {
if (this.unsaved) {
this.unsaved = false;
@@ -55,7 +56,7 @@
return true;
} else {
return false;
@@ -280,7 +_,7 @@
@@ -285,7 +_,7 @@
}
public boolean isUnsaved() {
@@ -64,7 +65,7 @@
}
public abstract ChunkStatus getPersistedStatus();
@@ -446,6 +_,22 @@
@@ -451,6 +_,22 @@
throw new ReportedException(crashReport);
}
}

View File

@@ -17,7 +17,7 @@
) {
+ // Paper start - StructuresLocateEvent
+ final org.bukkit.World bukkitWorld = level.getWorld();
+ final org.bukkit.Location origin = io.papermc.paper.util.MCUtil.toLocation(level, pos);
+ final org.bukkit.Location origin = org.bukkit.craftbukkit.util.CraftLocation.toBukkit(pos, level);
+ final List<org.bukkit.generator.structure.Structure> apiStructures = structure.stream().map(Holder::value).map(nms -> org.bukkit.craftbukkit.generator.structure.CraftStructure.minecraftToBukkit(nms)).toList();
+ if (!apiStructures.isEmpty()) {
+ final io.papermc.paper.event.world.StructuresLocateEvent event = new io.papermc.paper.event.world.StructuresLocateEvent(bukkitWorld, origin, apiStructures, searchRadius, skipKnownStructures);
@@ -27,7 +27,7 @@
+ if (event.getResult() != null) {
+ return Pair.of(io.papermc.paper.util.MCUtil.toBlockPos(event.getResult().pos()), level.registryAccess().lookupOrThrow(Registries.STRUCTURE).wrapAsHolder(org.bukkit.craftbukkit.generator.structure.CraftStructure.bukkitToMinecraft(event.getResult().structure())));
+ }
+ pos = io.papermc.paper.util.MCUtil.toBlockPosition(event.getOrigin());
+ pos = org.bukkit.craftbukkit.util.CraftLocation.toBlockPosition(event.getOrigin());
+ searchRadius = event.getRadius();
+ skipKnownStructures = event.shouldFindUnexplored();
+ structure = HolderSet.direct(api -> level.registryAccess().lookupOrThrow(Registries.STRUCTURE).wrapAsHolder(org.bukkit.craftbukkit.generator.structure.CraftStructure.bukkitToMinecraft(api)), event.getStructures());
@@ -36,7 +36,7 @@
ChunkGeneratorStructureState generatorState = level.getChunkSource().getGeneratorState();
Map<StructurePlacement, Set<Holder<Structure>>> map = new Object2ObjectArrayMap<>();
@@ -222,6 +_,7 @@
@@ -221,6 +_,7 @@
BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
for (ChunkPos chunkPos : ringPositionsFor) {
@@ -44,7 +44,7 @@
mutableBlockPos.set(SectionPos.sectionToBlockCoord(chunkPos.x, 8), 32, SectionPos.sectionToBlockCoord(chunkPos.z, 8));
double d1 = mutableBlockPos.distSqr(pos);
boolean flag = pair == null || d1 < d;
@@ -255,11 +_,15 @@
@@ -254,11 +_,15 @@
int spacing = spreadPlacement.spacing();
for (int i = -z; i <= z; i++) {
@@ -64,7 +64,7 @@
int i2 = x + spacing * i;
int i3 = y + spacing * i1;
ChunkPos potentialStructureChunk = spreadPlacement.getPotentialStructureChunk(seed, i2, i3);
@@ -312,7 +_,7 @@
@@ -311,7 +_,7 @@
}
}
@@ -73,7 +73,7 @@
ChunkPos pos = chunk.getPos();
if (!SharedConstants.debugVoidTerrain(pos)) {
SectionPos sectionPos = SectionPos.of(pos, level.getMinSectionY());
@@ -385,7 +_,14 @@
@@ -382,7 +_,14 @@
int i3 = ints[i2];
PlacedFeature placedFeature = stepFeatureData1.features().get(i3);
Supplier<String> supplier1 = () -> registry1.getResourceKey(placedFeature).map(Object::toString).orElseGet(placedFeature::toString);
@@ -89,7 +89,7 @@
try {
level.setCurrentlyGenerating(supplier1);
@@ -407,6 +_,32 @@
@@ -404,6 +_,32 @@
}
}
}
@@ -122,7 +122,7 @@
private static BoundingBox getWritableArea(ChunkAccess chunk) {
ChunkPos pos = chunk.getPos();
@@ -483,7 +_,7 @@
@@ -478,7 +_,7 @@
}
}
@@ -131,7 +131,7 @@
if (list.size() == 1) {
this.tryGenerateStructure(
list.get(0),
@@ -577,6 +_,14 @@
@@ -572,6 +_,14 @@
predicate
);
if (structureStart.isValid()) {

View File

@@ -1,15 +1,16 @@
--- a/net/minecraft/world/level/chunk/EmptyLevelChunk.java
+++ b/net/minecraft/world/level/chunk/EmptyLevelChunk.java
@@ -25,6 +_,12 @@
public BlockState getBlockState(BlockPos pos) {
@@ -26,6 +_,13 @@
return Blocks.VOID_AIR.defaultBlockState();
}
+ // Paper start
+ @Override
+ public BlockState getBlockState(final int x, final int y, final int z) {
+ return Blocks.VOID_AIR.defaultBlockState();
+ }
+ // Paper end
+
@Nullable
@Override
public BlockState setBlockState(BlockPos pos, BlockState state, int flags) {

View File

@@ -1,15 +1,16 @@
--- a/net/minecraft/world/level/chunk/ImposterProtoChunk.java
+++ b/net/minecraft/world/level/chunk/ImposterProtoChunk.java
@@ -56,6 +_,12 @@
public BlockState getBlockState(BlockPos pos) {
@@ -57,6 +_,13 @@
return this.wrapped.getBlockState(pos);
}
+ // Paper start
+ @Override
+ public final BlockState getBlockState(final int x, final int y, final int z) {
+ return this.wrapped.getBlockStateFinal(x, y, z);
+ }
+ // Paper end
+
@Override
public FluidState getFluidState(BlockPos pos) {
return this.wrapped.getFluidState(pos);

View File

@@ -1,6 +1,6 @@
--- a/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/net/minecraft/world/level/chunk/LevelChunk.java
@@ -76,7 +_,7 @@
@@ -77,7 +_,7 @@
};
private final Map<BlockPos, LevelChunk.RebindableTickingBlockEntityWrapper> tickersInLevel = Maps.newHashMap();
public boolean loaded;
@@ -9,7 +9,7 @@
@Nullable
private Supplier<FullChunkStatus> fullStatus;
@Nullable
@@ -85,6 +_,14 @@
@@ -86,6 +_,14 @@
private final LevelChunkTicks<Block> blockTicks;
private final LevelChunkTicks<Fluid> fluidTicks;
private LevelChunk.UnsavedListener unsavedListener = chunkPos -> {};
@@ -24,7 +24,7 @@
public LevelChunk(Level level, ChunkPos pos) {
this(level, pos, UpgradeData.EMPTY, new LevelChunkTicks<>(), new LevelChunkTicks<>(), 0L, null, null, null);
@@ -102,7 +_,7 @@
@@ -103,7 +_,7 @@
@Nullable BlendingData blendingData
) {
super(pos, data, level, level.registryAccess().lookupOrThrow(Registries.BIOME), inhabitedTime, sections, blendingData);
@@ -33,7 +33,7 @@
this.gameEventListenerRegistrySections = new Int2ObjectOpenHashMap<>();
for (Heightmap.Types types : Heightmap.Types.values()) {
@@ -154,6 +_,10 @@
@@ -155,6 +_,10 @@
this.skyLightSources = chunk.skyLightSources;
this.setLightCorrect(chunk.isLightCorrect());
this.markUnsaved();
@@ -44,7 +44,7 @@
}
public void setUnsavedListener(LevelChunk.UnsavedListener unsavedListener) {
@@ -162,6 +_,12 @@
@@ -163,6 +_,12 @@
unsavedListener.setUnsaved(this.chunkPos);
}
}
@@ -57,14 +57,16 @@
@Override
public void markUnsaved() {
@@ -195,8 +_,25 @@
@@ -196,8 +_,28 @@
: super.getListenerRegistry(sectionY);
}
+ // Paper start - Perf: Reduce instructions and provide final method
+ @Override
+ public BlockState getBlockState(final int x, final int y, final int z) {
+ return this.getBlockStateFinal(x, y, z);
+ }
+
+ public BlockState getBlockStateFinal(final int x, final int y, final int z) {
+ // Copied and modified from below
+ final int sectionIndex = this.getSectionIndex(y);
@@ -74,6 +76,7 @@
+ }
+ return this.sections[sectionIndex].states.get((y & 15) << 8 | (z & 15) << 4 | x & 15);
+ }
+
@Override
public BlockState getBlockState(BlockPos pos) {
+ if (true) {
@@ -83,19 +86,19 @@
int x = pos.getX();
int y = pos.getY();
int z = pos.getZ();
@@ -231,33 +_,54 @@
@@ -232,28 +_,42 @@
}
}
+ // Paper start - If loaded util
+ @Override
+ public final FluidState getFluidIfLoaded(BlockPos blockposition) {
+ return this.getFluidState(blockposition);
+ public final FluidState getFluidIfLoaded(BlockPos pos) {
+ return this.getFluidState(pos);
+ }
+
+ @Override
+ public final BlockState getBlockStateIfLoaded(BlockPos blockposition) {
+ return this.getBlockState(blockposition);
+ public final BlockState getBlockStateIfLoaded(BlockPos pos) {
+ return this.getBlockState(pos);
+ }
+ // Paper end
+
@@ -128,37 +131,16 @@
}
@Nullable
@Override
public BlockState setBlockState(BlockPos pos, BlockState state, boolean isMoving) {
+// CraftBukkit start
+ return this.setBlockState(pos, state, isMoving, true);
+ }
+
+ @Nullable
+ public BlockState setBlockState(BlockPos pos, BlockState state, boolean isMoving, boolean doPlace) {
+ // CraftBukkit end
int y = pos.getY();
LevelChunkSection section = this.getSection(this.getSectionIndex(y));
boolean hasOnlyAir = section.hasOnlyAir();
@@ -292,7 +_,7 @@
}
boolean hasBlockEntity = blockState.hasBlockEntity();
- if (!this.level.isClientSide) {
+ if (!this.level.isClientSide && !this.level.isBlockPlaceCancelled) { // Paper - prevent calling cleanup logic when undoing a block place upon a cancelled BlockPlaceEvent
blockState.onRemove(this.level, pos, state, isMoving);
} else if (!blockState.is(block) && hasBlockEntity) {
this.removeBlockEntity(pos);
@@ -301,7 +_,7 @@
@@ -313,7 +_,7 @@
if (!section.getBlockState(i, i1, i2).is(block)) {
return null;
} else {
- if (!this.level.isClientSide) {
+ if (!this.level.isClientSide && doPlace && (!this.level.captureBlockStates || block instanceof net.minecraft.world.level.block.BaseEntityBlock)) { // CraftBukkit - Don't place while processing the BlockPlaceEvent, unless it's a BlockContainer. Prevents blocks such as TNT from activating when cancelled.
state.onPlace(this.level, pos, blockState, isMoving);
- if (!this.level.isClientSide && (flags & 512) == 0) {
+ if (!this.level.isClientSide && (flags & 512) == 0 && (!this.level.captureBlockStates || block instanceof net.minecraft.world.level.block.BaseEntityBlock)) { // CraftBukkit - Don't place while processing the BlockPlaceEvent, unless it's a BlockContainer. Prevents blocks such as TNT from activating when cancelled.
state.onPlace(this.level, pos, blockState, flag1);
}
@@ -355,7 +_,12 @@
@@ -367,7 +_,12 @@
@Nullable
public BlockEntity getBlockEntity(BlockPos pos, LevelChunk.EntityCreationType creationType) {
@@ -172,7 +154,7 @@
if (blockEntity == null) {
CompoundTag compoundTag = this.pendingBlockEntities.remove(pos);
if (compoundTag != null) {
@@ -409,7 +_,13 @@
@@ -421,7 +_,13 @@
BlockPos blockPos = blockEntity.getBlockPos();
BlockState blockState = this.getBlockState(blockPos);
if (!blockState.hasBlockEntity()) {
@@ -187,7 +169,7 @@
} else {
BlockState blockState1 = blockEntity.getBlockState();
if (blockState != blockState1) {
@@ -457,6 +_,11 @@
@@ -469,6 +_,11 @@
public void removeBlockEntity(BlockPos pos) {
if (this.isInLevel()) {
BlockEntity blockEntity = this.blockEntities.remove(pos);
@@ -199,7 +181,7 @@
if (blockEntity != null) {
if (this.level instanceof ServerLevel serverLevel) {
this.removeGameEventListener(blockEntity, serverLevel);
@@ -499,6 +_,65 @@
@@ -511,6 +_,65 @@
}
}
@@ -265,7 +247,7 @@
public boolean isEmpty() {
return false;
}
@@ -711,23 +_,24 @@
@@ -719,23 +_,24 @@
if (this.blockEntity.getType().isValid(blockState)) {
this.ticker.tick(LevelChunk.this.level, this.blockEntity.getBlockPos(), blockState, this.blockEntity);
this.loggedInvalidBlockState = false;

View File

@@ -28,7 +28,7 @@
public FluidState getFluidState(int x, int y, int z) {
- return this.states.get(x, y, z).getFluidState();
+ return this.states.get(x, y, z).getFluidState(); // Paper - Perf: Optimise Chunk#getFluid; diff on change - we expect this to be effectively just getType(x, y, z).getFluid(). If this changes we need to check other patches that use IBlockData#getFluid.
+ return this.states.get(x, y, z).getFluidState(); // Paper - Perf: Optimise LevelChunk#getFluidState; diff on change - we expect this to be effectively just get(x, y, z).getFluidState(). If this changes we need to check other patches that use BlockBehaviour#getFluidState.
}
public void acquire() {

View File

@@ -1,18 +1,18 @@
--- a/net/minecraft/world/level/chunk/ProtoChunk.java
+++ b/net/minecraft/world/level/chunk/ProtoChunk.java
@@ -85,14 +_,32 @@
@@ -85,14 +_,33 @@
return new ChunkAccess.PackedTicks(this.blockTicks.pack(gametime), this.fluidTicks.pack(gametime));
}
+ // Paper start - If loaded util
+ @Override
+ public final FluidState getFluidIfLoaded(BlockPos blockposition) {
+ return this.getFluidState(blockposition);
+ public final FluidState getFluidIfLoaded(BlockPos pos) {
+ return this.getFluidState(pos);
+ }
+
+ @Override
+ public final BlockState getBlockStateIfLoaded(BlockPos blockposition) {
+ return this.getBlockState(blockposition);
+ public final BlockState getBlockStateIfLoaded(BlockPos pos) {
+ return this.getBlockState(pos);
+ }
+ // Paper end
+
@@ -20,8 +20,9 @@
public BlockState getBlockState(BlockPos pos) {
- int y = pos.getY();
+ // Paper start
+ return getBlockState(pos.getX(), pos.getY(), pos.getZ());
+ return this.getBlockState(pos.getX(), pos.getY(), pos.getZ());
+ }
+
+ public BlockState getBlockState(final int x, final int y, final int z) {
+ // Paper end
if (this.isOutsideBuildHeight(y)) {

View File

@@ -1,6 +1,6 @@
--- a/net/minecraft/world/level/chunk/UpgradeData.java
+++ b/net/minecraft/world/level/chunk/UpgradeData.java
@@ -113,6 +_,24 @@
@@ -87,6 +_,25 @@
}
}
@@ -22,10 +22,11 @@
+ }
+ }
+ // Paper end - filter out relocated neighbour ticks
+
public void upgrade(LevelChunk chunk) {
this.upgradeInside(chunk);
@@ -120,6 +_,10 @@
@@ -94,6 +_,10 @@
upgradeSides(chunk, direction8);
}
@@ -34,13 +35,13 @@
+ filterTickList(chunk.locX, chunk.locZ, this.neighborFluidTicks);
+ // Paper end - filter out relocated neighbour ticks
Level level = chunk.getLevel();
this.neighborBlockTicks.forEach(blockTicker -> {
Block block = blockTicker.type() == Blocks.AIR ? level.getBlockState(blockTicker.pos()).getBlock() : blockTicker.type();
@@ -129,6 +_,7 @@
Fluid fluid = fluidTicker.type() == Fluids.EMPTY ? level.getFluidState(fluidTicker.pos()).getType() : fluidTicker.type();
level.scheduleTick(fluidTicker.pos(), fluid, fluidTicker.delay(), fluidTicker.priority());
this.neighborBlockTicks.forEach(savedTick -> {
Block block = savedTick.type() == Blocks.AIR ? level.getBlockState(savedTick.pos()).getBlock() : savedTick.type();
@@ -103,6 +_,7 @@
Fluid fluid = savedTick.type() == Fluids.EMPTY ? level.getFluidState(savedTick.pos()).getType() : savedTick.type();
level.scheduleTick(savedTick.pos(), fluid, savedTick.delay(), savedTick.priority());
});
+ UpgradeData.BlockFixers.values(); // Paper - force the class init so that we don't access CHUNKY_FIXERS before all BlockFixers are initialised
CHUNKY_FIXERS.forEach(fixers -> fixers.processChunk(level));
CHUNKY_FIXERS.forEach(blockFixer -> blockFixer.processChunk(level));
}

View File

@@ -9,12 +9,9 @@
worldGenContext.generator()
.createStructures(
serverLevel.registryAccess(),
@@ -196,9 +_,60 @@
}, worldGenContext.mainThreadExecutor());
}
@@ -198,7 +_,58 @@
- private static void postLoadProtoChunk(ServerLevel level, List<CompoundTag> entityTags) {
+ public static void postLoadProtoChunk(ServerLevel level, List<CompoundTag> entityTags) { // Paper - public
public static void postLoadProtoChunk(ServerLevel level, List<CompoundTag> entityTags) {
if (!entityTags.isEmpty()) {
- level.addWorldGenChunkEntities(EntityType.loadEntitiesRecursive(entityTags, level, EntitySpawnReason.LOAD));
- }

View File

@@ -23,8 +23,8 @@
try {
+ // CraftBukkit start
+ if (version < 1466) {
+ CompoundTag level = chunkData.getCompound("Level");
+ if (level.getBoolean("TerrainPopulated") && !level.getBoolean("LightPopulated")) {
+ CompoundTag level = chunkData.getCompoundOrEmpty("Level");
+ if (level.getBooleanOr("TerrainPopulated", false) && !level.getBooleanOr("LightPopulated", false)) {
+ // Light is purged updating to 1.14+. We need to set light populated to true so the converter recognizes the chunk as being "full"
+ level.putBoolean("LightPopulated", true);
+ }
@@ -32,7 +32,7 @@
+ // CraftBukkit end
if (version < 1493) {
chunkData = DataFixTypes.CHUNK.update(this.fixerUpper, chunkData, version, 1493);
if (chunkData.getCompound("Level").getBoolean("hasLegacyStructureData")) {
if (chunkData.getCompound("Level").flatMap(compoundTag -> compoundTag.getBoolean("hasLegacyStructureData")).orElse(false)) {
@@ -57,8 +_,22 @@
}
}
@@ -42,7 +42,7 @@
+ boolean belowZeroGenerationInExistingChunks = (levelAccessor != null) ? ((net.minecraft.server.level.ServerLevel) levelAccessor).spigotConfig.belowZeroGenerationInExistingChunks : org.spigotmc.SpigotConfig.belowZeroGenerationInExistingChunks;
+
+ if (version <= 2730 && !belowZeroGenerationInExistingChunks) {
+ stopBelowZero = "full".equals(chunkData.getCompound("Level").getString("Status"));
+ stopBelowZero = "full".equals(chunkData.getCompound("Level").flatMap(l -> l.getString("Status")).orElse(null));
+ }
+ // Spigot end
+

View File

@@ -8,7 +8,7 @@
) {
public static final Codec<PalettedContainer<BlockState>> BLOCK_STATE_CODEC = PalettedContainer.codecRW(
Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState()
@@ -107,12 +_,39 @@
@@ -109,12 +_,38 @@
public static final String BLOCK_LIGHT_TAG = "BlockLight";
public static final String SKY_LIGHT_TAG = "SkyLight";
@@ -17,10 +17,10 @@
+ public static ChunkPos getChunkCoordinate(final CompoundTag chunkData) {
+ final int dataVersion = ChunkStorage.getVersion(chunkData);
+ if (dataVersion < 2842) { // Level tag is removed after this version
+ final CompoundTag levelData = chunkData.getCompound("Level");
+ return new ChunkPos(levelData.getInt("xPos"), levelData.getInt("zPos"));
+ final CompoundTag levelData = chunkData.getCompoundOrEmpty("Level");
+ return new ChunkPos(levelData.getIntOr("xPos", 0), levelData.getIntOr("zPos", 0));
+ } else {
+ return new ChunkPos(chunkData.getInt("xPos"), chunkData.getInt("zPos"));
+ return new ChunkPos(chunkData.getIntOr("xPos", 0), chunkData.getIntOr("zPos", 0));
+ }
+ }
+ // Paper end - guard against serializing mismatching coordinates
@@ -32,50 +32,49 @@
+
@Nullable
public static SerializableChunkData parse(LevelHeightAccessor levelHeightAccessor, RegistryAccess registries, CompoundTag tag) {
if (!tag.contains("Status", 8)) {
if (tag.getString("Status").isEmpty()) {
return null;
} else {
- ChunkPos chunkPos = new ChunkPos(tag.getInt("xPos"), tag.getInt("zPos"));
- ChunkPos chunkPos = new ChunkPos(tag.getIntOr("xPos", 0), tag.getIntOr("zPos", 0));
+ // Paper start - Do not let the server load chunks from newer versions
+ if (tag.contains("DataVersion", net.minecraft.nbt.Tag.TAG_ANY_NUMERIC)) {
+ final int dataVersion = tag.getInt("DataVersion");
+ tag.getInt("DataVersion").ifPresent(dataVersion -> {
+ if (!JUST_CORRUPT_IT && dataVersion > CURRENT_DATA_VERSION) {
+ new RuntimeException("Server attempted to load chunk saved with newer version of minecraft! " + dataVersion + " > " + CURRENT_DATA_VERSION).printStackTrace();
+ System.exit(1);
+ }
+ }
+ });
+ // Paper end - Do not let the server load chunks from newer versions
+ ChunkPos chunkPos = new ChunkPos(tag.getInt("xPos"), tag.getInt("zPos")); // Paper - guard against serializing mismatching coordinates; diff on change, see ChunkSerializer#getChunkCoordinate
long _long = tag.getLong("LastUpdate");
long _long1 = tag.getLong("InhabitedTime");
ChunkStatus chunkStatus = ChunkStatus.byName(tag.getString("Status"));
@@ -181,7 +_,7 @@
ListTag list7 = tag.getList("sections", 10);
List<SerializableChunkData.SectionData> list8 = new ArrayList<>(list7.size());
+ ChunkPos chunkPos = new ChunkPos(tag.getIntOr("xPos", 0), tag.getIntOr("zPos", 0)); // Paper - guard against serializing mismatching coordinates; diff on change, see ChunkSerializer#getChunkCoordinate
long longOr = tag.getLongOr("LastUpdate", 0L);
long longOr1 = tag.getLongOr("InhabitedTime", 0L);
ChunkStatus chunkStatus = tag.read("Status", ChunkStatus.CODEC).orElse(ChunkStatus.EMPTY);
@@ -154,7 +_,7 @@
ListTag listOrEmpty2 = tag.getListOrEmpty("sections");
List<SerializableChunkData.SectionData> list5 = new ArrayList<>(listOrEmpty2.size());
Registry<Biome> registry = registries.lookupOrThrow(Registries.BIOME);
- Codec<PalettedContainerRO<Holder<Biome>>> codec = makeBiomeCodec(registry);
+ Codec<PalettedContainer<Holder<Biome>>> codec = makeBiomeCodecRW(registry); // CraftBukkit - read/write
for (int i2 = 0; i2 < list7.size(); i2++) {
CompoundTag compound2 = list7.getCompound(i2);
@@ -199,7 +_,7 @@
);
}
- PalettedContainerRO<Holder<Biome>> palettedContainerRo;
+ PalettedContainer<Holder<Biome>> palettedContainerRo; // CraftBukkit - read/write
if (compound2.contains("biomes", 10)) {
palettedContainerRo = codec.parse(NbtOps.INSTANCE, compound2.getCompound("biomes"))
.promotePartial(string -> logErrors(chunkPos, _byte, string))
@@ -239,6 +_,7 @@
list5,
list6,
compound1
for (int i2 = 0; i2 < listOrEmpty2.size(); i2++) {
Optional<CompoundTag> compound = listOrEmpty2.getCompound(i2);
@@ -174,7 +_,7 @@
Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES
)
);
- PalettedContainerRO<Holder<Biome>> palettedContainerRo = compoundTag.getCompound("biomes")
+ PalettedContainer<Holder<Biome>> palettedContainerRo = compoundTag.getCompound("biomes") // CraftBukkit - read/write
.map(
compoundTag1 -> codec.parse(NbtOps.INSTANCE, compoundTag1)
.promotePartial(string -> logErrors(chunkPos, byteOr, string))
@@ -215,6 +_,7 @@
list3,
list4,
compoundOrEmpty
+ , tag.get("ChunkBukkitValues") // CraftBukkit - ChunkBukkitValues
);
}
}
@@ -316,6 +_,12 @@
@@ -292,6 +_,12 @@
}
}
@@ -88,21 +87,7 @@
chunkAccess.setLightCorrect(this.lightCorrect);
EnumSet<Heightmap.Types> set = EnumSet.noneOf(Heightmap.Types.class);
@@ -346,6 +_,13 @@
}
for (CompoundTag compoundTag : this.blockEntities) {
+ // Paper start - do not read tile entities positioned outside the chunk
+ final BlockPos blockposition = BlockEntity.getPosFromTag(compoundTag);
+ if ((blockposition.getX() >> 4) != this.chunkPos.x || (blockposition.getZ() >> 4) != this.chunkPos.z) {
+ LOGGER.warn("Tile entity serialized in chunk {} in world '{}' positioned at {} is located outside of the chunk", this.chunkPos, level.getWorld().getName(), blockposition);
+ continue;
+ }
+ // Paper end - do not read tile entities positioned outside the chunk
protoChunk1.setBlockEntityNbt(compoundTag);
}
@@ -370,6 +_,12 @@
@@ -346,6 +_,12 @@
);
}
@@ -115,7 +100,7 @@
public static SerializableChunkData copyOf(ServerLevel level, ChunkAccess chunk) {
if (!chunk.canBeSerialized()) {
throw new IllegalArgumentException("Chunk can't be serialized: " + chunk);
@@ -428,6 +_,12 @@
@@ -404,6 +_,12 @@
CompoundTag compoundTag = packStructureData(
StructurePieceSerializationContext.fromLevel(level), pos, chunk.getAllStarts(), chunk.getAllReferences()
);
@@ -128,7 +113,7 @@
return new SerializableChunkData(
level.registryAccess().lookupOrThrow(Registries.BIOME),
pos,
@@ -447,6 +_,7 @@
@@ -423,6 +_,7 @@
list2,
list1,
compoundTag
@@ -136,7 +121,7 @@
);
}
}
@@ -525,6 +_,11 @@
@@ -489,6 +_,11 @@
this.heightmaps.forEach((types, longs) -> compoundTag2.put(types.getSerializationKey(), new LongArrayTag(longs)));
compoundTag.put("Heightmaps", compoundTag2);
compoundTag.put("structures", this.structureData);
@@ -148,26 +133,12 @@
return compoundTag;
}
@@ -562,6 +_,13 @@
chunk.setBlockEntityNbt(compoundTag);
} else {
BlockPos posFromTag = BlockEntity.getPosFromTag(compoundTag);
+ // Paper start - do not read tile entities positioned outside the chunk
+ ChunkPos chunkPos = chunk.getPos();
+ if ((posFromTag.getX() >> 4) != chunkPos.x || (posFromTag.getZ() >> 4) != chunkPos.z) {
+ LOGGER.warn("Tile entity serialized in chunk " + chunkPos + " in world '" + level.getWorld().getName() + "' positioned at " + posFromTag + " is located outside of the chunk");
+ continue;
+ }
+ // Paper end - do not read tile entities positioned outside the chunk
BlockEntity blockEntity = BlockEntity.loadStatic(posFromTag, chunk.getBlockState(posFromTag), compoundTag, level.registryAccess());
if (blockEntity != null) {
chunk.setBlockEntity(blockEntity);
@@ -610,6 +_,12 @@
@@ -562,6 +_,12 @@
} else {
StructureStart structureStart = StructureStart.loadStaticStart(context, compound.getCompound(string), seed);
StructureStart structureStart = StructureStart.loadStaticStart(context, compoundOrEmpty.getCompoundOrEmpty(string), seed);
if (structureStart != null) {
+ // CraftBukkit start - load persistent data for structure start
+ net.minecraft.nbt.Tag persistentBase = compound.getCompound(string).get("StructureBukkitValues");
+ net.minecraft.nbt.Tag persistentBase = compoundOrEmpty.getCompoundOrEmpty(string).get("StructureBukkitValues");
+ if (persistentBase instanceof CompoundTag compoundTag) {
+ structureStart.persistentDataContainer.putAll(compoundTag);
+ }