net.minecraft.world.level.chunk.storage
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
--- a/net/minecraft/world/level/chunk/storage/ChunkStorage.java
|
||||
+++ b/net/minecraft/world/level/chunk/storage/ChunkStorage.java
|
||||
@@ -15,10 +_,16 @@
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.NbtUtils;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
+import net.minecraft.server.level.ServerChunkCache;
|
||||
+import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.util.datafix.DataFixTypes;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
-import net.minecraft.world.level.Level;
|
||||
+import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||
+// CraftBukkit start
|
||||
+import java.util.concurrent.ExecutionException;
|
||||
+import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
+import net.minecraft.world.level.dimension.LevelStem;
|
||||
import net.minecraft.world.level.levelgen.structure.LegacyStructureDataHandler;
|
||||
import net.minecraft.world.level.storage.DimensionDataStorage;
|
||||
|
||||
@@ -38,17 +_,63 @@
|
||||
return this.worker.isOldChunkAround(pos, radius);
|
||||
}
|
||||
|
||||
+ // CraftBukkit start
|
||||
+ private boolean check(ServerChunkCache cps, int x, int z) {
|
||||
+ if (true) return true; // Paper - Perf: this isn't even needed anymore, light is purged updating to 1.14+, why are we holding up the conversion process reading chunk data off disk - return true, we need to set light populated to true so the converter recognizes the chunk as being "full"
|
||||
+ ChunkPos pos = new ChunkPos(x, z);
|
||||
+ if (cps != null) {
|
||||
+ com.google.common.base.Preconditions.checkState(org.bukkit.Bukkit.isPrimaryThread(), "primary thread");
|
||||
+ if (cps.hasChunk(x, z)) {
|
||||
+ return true;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ CompoundTag nbt;
|
||||
+ try {
|
||||
+ nbt = this.read(pos).get().orElse(null);
|
||||
+ } catch (InterruptedException | ExecutionException ex) {
|
||||
+ throw new RuntimeException(ex);
|
||||
+ }
|
||||
+ if (nbt != null) {
|
||||
+ CompoundTag level = nbt.getCompound("Level");
|
||||
+ if (level.getBoolean("TerrainPopulated")) {
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ ChunkStatus status = ChunkStatus.byName(level.getString("Status"));
|
||||
+ if (status != null && status.isOrAfter(ChunkStatus.FEATURES)) {
|
||||
+ return true;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
public CompoundTag upgradeChunkTag(
|
||||
- ResourceKey<Level> levelKey,
|
||||
+ ResourceKey<LevelStem> levelKey,
|
||||
Supplier<DimensionDataStorage> storage,
|
||||
CompoundTag chunkData,
|
||||
- Optional<ResourceKey<MapCodec<? extends ChunkGenerator>>> chunkGeneratorKey
|
||||
+ Optional<ResourceKey<MapCodec<? extends ChunkGenerator>>> chunkGeneratorKey,
|
||||
+ ChunkPos pos,
|
||||
+ @Nullable LevelAccessor generatoraccess
|
||||
+ // CraftBukkit end
|
||||
) {
|
||||
int version = getVersion(chunkData);
|
||||
if (version == SharedConstants.getCurrentVersion().getDataVersion().getVersion()) {
|
||||
return chunkData;
|
||||
} else {
|
||||
try {
|
||||
+ // CraftBukkit start
|
||||
+ if (version < 1466) {
|
||||
+ CompoundTag level = chunkData.getCompound("Level");
|
||||
+ if (level.getBoolean("TerrainPopulated") && !level.getBoolean("LightPopulated")) {
|
||||
+ ServerChunkCache cps = (generatoraccess == null) ? null : ((ServerLevel) generatoraccess).getChunkSource();
|
||||
+ if (this.check(cps, pos.x - 1, pos.z) && this.check(cps, pos.x - 1, pos.z - 1) && this.check(cps, pos.x, pos.z - 1)) {
|
||||
+ level.putBoolean("LightPopulated", true);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
if (version < 1493) {
|
||||
chunkData = DataFixTypes.CHUNK.update(this.fixerUpper, chunkData, version, 1493);
|
||||
if (chunkData.getCompound("Level").getBoolean("hasLegacyStructureData")) {
|
||||
@@ -57,8 +_,22 @@
|
||||
}
|
||||
}
|
||||
|
||||
+ // Spigot start - SPIGOT-6806: Quick and dirty way to prevent below zero generation in old chunks, by setting the status to heightmap instead of empty
|
||||
+ boolean stopBelowZero = false;
|
||||
+ boolean belowZeroGenerationInExistingChunks = (generatoraccess != null) ? ((ServerLevel) generatoraccess).spigotConfig.belowZeroGenerationInExistingChunks : org.spigotmc.SpigotConfig.belowZeroGenerationInExistingChunks;
|
||||
+
|
||||
+ if (version <= 2730 && !belowZeroGenerationInExistingChunks) {
|
||||
+ stopBelowZero = "full".equals(chunkData.getCompound("Level").getString("Status"));
|
||||
+ }
|
||||
+ // Spigot end
|
||||
+
|
||||
injectDatafixingContext(chunkData, levelKey, chunkGeneratorKey);
|
||||
chunkData = DataFixTypes.CHUNK.updateToCurrentVersion(this.fixerUpper, chunkData, Math.max(1493, version));
|
||||
+ // Spigot start
|
||||
+ if (stopBelowZero) {
|
||||
+ chunkData.putString("Status", net.minecraft.core.registries.BuiltInRegistries.CHUNK_STATUS.getKey(ChunkStatus.SPAWN).toString());
|
||||
+ }
|
||||
+ // Spigot end
|
||||
removeDatafixingContext(chunkData);
|
||||
NbtUtils.addCurrentDataVersion(chunkData);
|
||||
return chunkData;
|
||||
@@ -71,7 +_,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
- private LegacyStructureDataHandler getLegacyStructureHandler(ResourceKey<Level> level, Supplier<DimensionDataStorage> storage) {
|
||||
+ private LegacyStructureDataHandler getLegacyStructureHandler(ResourceKey<LevelStem> level, Supplier<DimensionDataStorage> storage) { // CraftBukkit
|
||||
LegacyStructureDataHandler legacyStructureDataHandler = this.legacyStructureHandler;
|
||||
if (legacyStructureDataHandler == null) {
|
||||
synchronized (this) {
|
||||
@@ -86,7 +_,7 @@
|
||||
}
|
||||
|
||||
public static void injectDatafixingContext(
|
||||
- CompoundTag chunkData, ResourceKey<Level> levelKey, Optional<ResourceKey<MapCodec<? extends ChunkGenerator>>> chunkGeneratorKey
|
||||
+ CompoundTag chunkData, ResourceKey<LevelStem> levelKey, Optional<ResourceKey<MapCodec<? extends ChunkGenerator>>> chunkGeneratorKey // CraftBukkit
|
||||
) {
|
||||
CompoundTag compoundTag = new CompoundTag();
|
||||
compoundTag.putString("dimension", levelKey.location().toString());
|
||||
@@ -107,8 +_,19 @@
|
||||
}
|
||||
|
||||
public CompletableFuture<Void> write(ChunkPos pos, Supplier<CompoundTag> tagSupplier) {
|
||||
+ // Paper start - guard against possible chunk pos desync
|
||||
+ final Supplier<CompoundTag> guardedPosCheck = () -> {
|
||||
+ CompoundTag nbt = tagSupplier.get();
|
||||
+ if (nbt != null && !pos.equals(SerializableChunkData.getChunkCoordinate(nbt))) {
|
||||
+ final String world = (ChunkStorage.this instanceof net.minecraft.server.level.ChunkMap) ? ((net.minecraft.server.level.ChunkMap) ChunkStorage.this).level.getWorld().getName() : null;
|
||||
+ throw new IllegalArgumentException("Chunk coordinate and serialized data do not have matching coordinates, trying to serialize coordinate " + pos
|
||||
+ + " but compound says coordinate is " + SerializableChunkData.getChunkCoordinate(nbt) + (world == null ? " for an unknown world" : (" for world: " + world)));
|
||||
+ }
|
||||
+ return nbt;
|
||||
+ };
|
||||
+ // Paper end - guard against possible chunk pos desync
|
||||
this.handleLegacyStructureIndex(pos);
|
||||
- return this.worker.store(pos, tagSupplier);
|
||||
+ return this.worker.store(pos, guardedPosCheck); // Paper - guard against possible chunk pos desync
|
||||
}
|
||||
|
||||
protected void handleLegacyStructureIndex(ChunkPos chunkPos) {
|
||||
@@ -0,0 +1,73 @@
|
||||
--- a/net/minecraft/world/level/chunk/storage/RegionFile.java
|
||||
+++ b/net/minecraft/world/level/chunk/storage/RegionFile.java
|
||||
@@ -1,3 +_,4 @@
|
||||
+// mc-dev import
|
||||
package net.minecraft.world.level.chunk.storage;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
@@ -46,7 +_,7 @@
|
||||
protected final RegionBitmap usedSectors = new RegionBitmap();
|
||||
|
||||
public RegionFile(RegionStorageInfo info, Path path, Path externalFileDir, boolean sync) throws IOException {
|
||||
- this(info, path, externalFileDir, RegionFileVersion.getSelected(), sync);
|
||||
+ this(info, path, externalFileDir, RegionFileVersion.getCompressionFormat(), sync); // Paper - Configurable region compression format
|
||||
}
|
||||
|
||||
public RegionFile(RegionStorageInfo info, Path path, Path externalFileDir, RegionFileVersion version, boolean sync) throws IOException {
|
||||
@@ -82,6 +_,14 @@
|
||||
if (i2 != 0) {
|
||||
int sectorNumber = getSectorNumber(i2);
|
||||
int numSectors = getNumSectors(i2);
|
||||
+ // Spigot start
|
||||
+ if (numSectors == 255) {
|
||||
+ // We're maxed out, so we need to read the proper length from the section
|
||||
+ ByteBuffer realLen = ByteBuffer.allocate(4);
|
||||
+ this.file.read(realLen, sectorNumber * 4096);
|
||||
+ numSectors = (realLen.getInt(0) + 4) / 4096 + 1;
|
||||
+ }
|
||||
+ // Spigot end
|
||||
if (sectorNumber < 2) {
|
||||
LOGGER.warn("Region file {} has invalid sector at index: {}; sector {} overlaps with header", path, i1, sectorNumber);
|
||||
this.offsets.put(i1, 0);
|
||||
@@ -117,6 +_,13 @@
|
||||
} else {
|
||||
int sectorNumber = getSectorNumber(offset);
|
||||
int numSectors = getNumSectors(offset);
|
||||
+ // Spigot start
|
||||
+ if (numSectors == 255) {
|
||||
+ ByteBuffer realLen = ByteBuffer.allocate(4);
|
||||
+ this.file.read(realLen, sectorNumber * 4096);
|
||||
+ numSectors = (realLen.getInt(0) + 4) / 4096 + 1;
|
||||
+ }
|
||||
+ // Spigot end
|
||||
int i = numSectors * 4096;
|
||||
ByteBuffer byteBuffer = ByteBuffer.allocate(i);
|
||||
this.file.read(byteBuffer, sectorNumber * 4096);
|
||||
@@ -260,6 +_,7 @@
|
||||
return true;
|
||||
}
|
||||
} catch (IOException var9) {
|
||||
+ com.destroystokyo.paper.exception.ServerInternalException.reportInternalException(var9); // Paper - ServerExceptionEvent
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -331,13 +_,18 @@
|
||||
try (FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.CREATE, StandardOpenOption.WRITE)) {
|
||||
chunkData.position(5);
|
||||
fileChannel.write(chunkData);
|
||||
+ // Paper start - ServerExceptionEvent
|
||||
+ } catch (Throwable throwable) {
|
||||
+ com.destroystokyo.paper.exception.ServerInternalException.reportInternalException(throwable);
|
||||
+ throw throwable;
|
||||
+ // Paper end - ServerExceptionEvent
|
||||
}
|
||||
|
||||
return () -> Files.move(path, externalChunkFile, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
|
||||
private void writeHeader() throws IOException {
|
||||
- this.header.position(0);
|
||||
+ ((java.nio.Buffer) this.header).position(0); // CraftBukkit - decompile error
|
||||
this.file.write(this.header, 0L);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
--- a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
|
||||
+++ b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
|
||||
@@ -28,18 +_,19 @@
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
- private RegionFile getRegionFile(ChunkPos chunkPos) throws IOException {
|
||||
+ @org.jetbrains.annotations.Contract("_, false -> !null") @Nullable private RegionFile getRegionFile(ChunkPos chunkPos, boolean existingOnly) throws IOException { // CraftBukkit
|
||||
long packedChunkPos = ChunkPos.asLong(chunkPos.getRegionX(), chunkPos.getRegionZ());
|
||||
RegionFile regionFile = this.regionCache.getAndMoveToFirst(packedChunkPos);
|
||||
if (regionFile != null) {
|
||||
return regionFile;
|
||||
} else {
|
||||
- if (this.regionCache.size() >= 256) {
|
||||
+ if (this.regionCache.size() >= io.papermc.paper.configuration.GlobalConfiguration.get().misc.regionFileCacheSize) { // Paper - Sanitise RegionFileCache and make configurable
|
||||
this.regionCache.removeLast().close();
|
||||
}
|
||||
|
||||
FileUtil.createDirectoriesSafe(this.folder);
|
||||
Path path = this.folder.resolve("r." + chunkPos.getRegionX() + "." + chunkPos.getRegionZ() + ".mca");
|
||||
+ if (existingOnly && !java.nio.file.Files.exists(path)) return null; // CraftBukkit
|
||||
RegionFile regionFile1 = new RegionFile(this.info, path, this.folder, this.sync);
|
||||
this.regionCache.putAndMoveToFirst(packedChunkPos, regionFile1);
|
||||
return regionFile1;
|
||||
@@ -48,7 +_,12 @@
|
||||
|
||||
@Nullable
|
||||
public CompoundTag read(ChunkPos chunkPos) throws IOException {
|
||||
- RegionFile regionFile = this.getRegionFile(chunkPos);
|
||||
+ // CraftBukkit start - SPIGOT-5680: There's no good reason to preemptively create files on read, save that for writing
|
||||
+ RegionFile regionFile = this.getRegionFile(chunkPos, true);
|
||||
+ if (regionFile == null) {
|
||||
+ return null;
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
|
||||
CompoundTag var4;
|
||||
try (DataInputStream chunkDataInputStream = regionFile.getChunkDataInputStream(chunkPos)) {
|
||||
@@ -63,7 +_,12 @@
|
||||
}
|
||||
|
||||
public void scanChunk(ChunkPos chunkPos, StreamTagVisitor visitor) throws IOException {
|
||||
- RegionFile regionFile = this.getRegionFile(chunkPos);
|
||||
+ // CraftBukkit start - SPIGOT-5680: There's no good reason to preemptively create files on read, save that for writing
|
||||
+ RegionFile regionFile = this.getRegionFile(chunkPos, true);
|
||||
+ if (regionFile == null) {
|
||||
+ return;
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
|
||||
try (DataInputStream chunkDataInputStream = regionFile.getChunkDataInputStream(chunkPos)) {
|
||||
if (chunkDataInputStream != null) {
|
||||
@@ -73,7 +_,7 @@
|
||||
}
|
||||
|
||||
protected void write(ChunkPos chunkPos, @Nullable CompoundTag chunkData) throws IOException {
|
||||
- RegionFile regionFile = this.getRegionFile(chunkPos);
|
||||
+ RegionFile regionFile = this.getRegionFile(chunkPos, false); // CraftBukkit
|
||||
if (chunkData == null) {
|
||||
regionFile.clear(chunkPos);
|
||||
} else {
|
||||
@@ -0,0 +1,18 @@
|
||||
--- a/net/minecraft/world/level/chunk/storage/RegionFileVersion.java
|
||||
+++ b/net/minecraft/world/level/chunk/storage/RegionFileVersion.java
|
||||
@@ -61,6 +_,15 @@
|
||||
private final RegionFileVersion.StreamWrapper<InputStream> inputWrapper;
|
||||
private final RegionFileVersion.StreamWrapper<OutputStream> outputWrapper;
|
||||
|
||||
+ // Paper start - Configurable region compression format
|
||||
+ public static RegionFileVersion getCompressionFormat() {
|
||||
+ return switch (io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.compressionFormat) {
|
||||
+ case GZIP -> VERSION_GZIP;
|
||||
+ case ZLIB -> VERSION_DEFLATE;
|
||||
+ case NONE -> VERSION_NONE;
|
||||
+ };
|
||||
+ }
|
||||
+ // Paper end - Configurable region compression format
|
||||
private RegionFileVersion(
|
||||
int id,
|
||||
@Nullable String optionName,
|
||||
@@ -0,0 +1,177 @@
|
||||
--- a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
|
||||
+++ b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
|
||||
@@ -91,6 +_,7 @@
|
||||
List<CompoundTag> entities,
|
||||
List<CompoundTag> blockEntities,
|
||||
CompoundTag structureData
|
||||
+ , @Nullable net.minecraft.nbt.Tag persistentDataContainer // CraftBukkit - persistentDataContainer
|
||||
) {
|
||||
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 @@
|
||||
public static final String BLOCK_LIGHT_TAG = "BlockLight";
|
||||
public static final String SKY_LIGHT_TAG = "SkyLight";
|
||||
|
||||
+ // Paper start - guard against serializing mismatching coordinates
|
||||
+ // TODO Note: This needs to be re-checked each update
|
||||
+ 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"));
|
||||
+ } else {
|
||||
+ return new ChunkPos(chunkData.getInt("xPos"), chunkData.getInt("zPos"));
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - guard against serializing mismatching coordinates
|
||||
+
|
||||
+ // Paper start - Do not let the server load chunks from newer versions
|
||||
+ private static final int CURRENT_DATA_VERSION = net.minecraft.SharedConstants.getCurrentVersion().getDataVersion().getVersion();
|
||||
+ private static final boolean JUST_CORRUPT_IT = Boolean.getBoolean("Paper.ignoreWorldDataVersion");
|
||||
+ // Paper end - Do not let the server load chunks from newer versions
|
||||
+
|
||||
@Nullable
|
||||
public static SerializableChunkData parse(LevelHeightAccessor levelHeightAccessor, RegistryAccess registries, CompoundTag tag) {
|
||||
if (!tag.contains("Status", 8)) {
|
||||
return null;
|
||||
} else {
|
||||
- ChunkPos chunkPos = new ChunkPos(tag.getInt("xPos"), tag.getInt("zPos"));
|
||||
+ // 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");
|
||||
+ 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());
|
||||
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
|
||||
+ , tag.get("ChunkBukkitValues") // CraftBukkit - ChunkBukkitValues
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -316,6 +_,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
+ // CraftBukkit start - load chunk persistent data from nbt - SPIGOT-6814: Already load PDC here to account for 1.17 to 1.18 chunk upgrading.
|
||||
+ if (this.persistentDataContainer instanceof CompoundTag) {
|
||||
+ chunkAccess.persistentDataContainer.putAll((CompoundTag) this.persistentDataContainer);
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
+
|
||||
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 @@
|
||||
);
|
||||
}
|
||||
|
||||
+ // CraftBukkit start - read/write
|
||||
+ private static Codec<PalettedContainer<Holder<Biome>>> makeBiomeCodecRW(Registry<Biome> iregistry) {
|
||||
+ return PalettedContainer.codecRW(iregistry.asHolderIdMap(), iregistry.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, iregistry.getOrThrow(Biomes.PLAINS));
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
+
|
||||
public static SerializableChunkData copyOf(ServerLevel level, ChunkAccess chunk) {
|
||||
if (!chunk.canBeSerialized()) {
|
||||
throw new IllegalArgumentException("Chunk can't be serialized: " + chunk);
|
||||
@@ -428,6 +_,12 @@
|
||||
CompoundTag compoundTag = packStructureData(
|
||||
StructurePieceSerializationContext.fromLevel(level), pos, chunk.getAllStarts(), chunk.getAllReferences()
|
||||
);
|
||||
+ // CraftBukkit start - store chunk persistent data in nbt
|
||||
+ CompoundTag persistentDataContainer = null;
|
||||
+ if (!chunk.persistentDataContainer.isEmpty()) { // SPIGOT-6814: Always save PDC to account for 1.17 to 1.18 chunk upgrading.
|
||||
+ persistentDataContainer = chunk.persistentDataContainer.toTagCompound();
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
return new SerializableChunkData(
|
||||
level.registryAccess().lookupOrThrow(Registries.BIOME),
|
||||
pos,
|
||||
@@ -447,6 +_,7 @@
|
||||
list2,
|
||||
list1,
|
||||
compoundTag
|
||||
+ , persistentDataContainer // CraftBukkit - persistentDataContainer
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -525,6 +_,11 @@
|
||||
this.heightmaps.forEach((types, longs) -> compoundTag2.put(types.getSerializationKey(), new LongArrayTag(longs)));
|
||||
compoundTag.put("Heightmaps", compoundTag2);
|
||||
compoundTag.put("structures", this.structureData);
|
||||
+ // CraftBukkit start - store chunk persistent data in nbt
|
||||
+ if (this.persistentDataContainer != null) { // SPIGOT-6814: Always save PDC to account for 1.17 to 1.18 chunk upgrading.
|
||||
+ compoundTag.put("ChunkBukkitValues", this.persistentDataContainer);
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
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 @@
|
||||
} else {
|
||||
StructureStart structureStart = StructureStart.loadStaticStart(context, compound.getCompound(string), seed);
|
||||
if (structureStart != null) {
|
||||
+ // CraftBukkit start - load persistent data for structure start
|
||||
+ net.minecraft.nbt.Tag persistentBase = compound.getCompound(string).get("StructureBukkitValues");
|
||||
+ if (persistentBase instanceof CompoundTag) {
|
||||
+ structureStart.persistentDataContainer.putAll((CompoundTag) persistentBase);
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
map.put(structure, structureStart);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user