mirror of
https://github.com/PaperMC/Paper.git
synced 2024-12-27 23:10:16 +01:00
net.minecraft.world.level.chunk.storage
This commit is contained in:
parent
1dd7ab9203
commit
2546348b9d
9 changed files with 461 additions and 570 deletions
|
@ -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 {
|
|
@ -1,6 +1,6 @@
|
|||
--- a/net/minecraft/world/level/chunk/storage/RegionFileVersion.java
|
||||
+++ b/net/minecraft/world/level/chunk/storage/RegionFileVersion.java
|
||||
@@ -58,6 +58,15 @@
|
||||
@@ -61,6 +_,15 @@
|
||||
private final RegionFileVersion.StreamWrapper<InputStream> inputWrapper;
|
||||
private final RegionFileVersion.StreamWrapper<OutputStream> outputWrapper;
|
||||
|
||||
|
@ -15,4 +15,4 @@
|
|||
+ // Paper end - Configurable region compression format
|
||||
private RegionFileVersion(
|
||||
int id,
|
||||
@Nullable String name,
|
||||
@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);
|
||||
}
|
||||
}
|
|
@ -1,158 +0,0 @@
|
|||
--- a/net/minecraft/world/level/chunk/storage/ChunkStorage.java
|
||||
+++ b/net/minecraft/world/level/chunk/storage/ChunkStorage.java
|
||||
@@ -15,10 +15,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;
|
||||
|
||||
@@ -39,27 +45,86 @@
|
||||
return this.worker.isOldChunkAround(chunkPos, checkRadius);
|
||||
}
|
||||
|
||||
- public CompoundTag upgradeChunkTag(ResourceKey<Level> worldKey, Supplier<DimensionDataStorage> persistentStateManagerFactory, CompoundTag nbt, Optional<ResourceKey<MapCodec<? extends ChunkGenerator>>> generatorCodecKey) {
|
||||
- int i = ChunkStorage.getVersion(nbt);
|
||||
+ // 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<LevelStem> resourcekey, Supplier<DimensionDataStorage> supplier, CompoundTag nbttagcompound, Optional<ResourceKey<MapCodec<? extends ChunkGenerator>>> optional, ChunkPos pos, @Nullable LevelAccessor generatoraccess) {
|
||||
+ // CraftBukkit end
|
||||
+ int i = ChunkStorage.getVersion(nbttagcompound);
|
||||
+
|
||||
if (i == SharedConstants.getCurrentVersion().getDataVersion().getVersion()) {
|
||||
- return nbt;
|
||||
+ return nbttagcompound;
|
||||
} else {
|
||||
try {
|
||||
+ // CraftBukkit start
|
||||
+ if (i < 1466) {
|
||||
+ CompoundTag level = nbttagcompound.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 (i < 1493) {
|
||||
- nbt = DataFixTypes.CHUNK.update(this.fixerUpper, nbt, i, 1493);
|
||||
- if (nbt.getCompound("Level").getBoolean("hasLegacyStructureData")) {
|
||||
- LegacyStructureDataHandler persistentstructurelegacy = this.getLegacyStructureHandler(worldKey, persistentStateManagerFactory);
|
||||
+ nbttagcompound = DataFixTypes.CHUNK.update(this.fixerUpper, nbttagcompound, i, 1493);
|
||||
+ if (nbttagcompound.getCompound("Level").getBoolean("hasLegacyStructureData")) {
|
||||
+ LegacyStructureDataHandler persistentstructurelegacy = this.getLegacyStructureHandler(resourcekey, supplier);
|
||||
|
||||
- nbt = persistentstructurelegacy.updateFromLegacy(nbt);
|
||||
+ nbttagcompound = persistentstructurelegacy.updateFromLegacy(nbttagcompound);
|
||||
}
|
||||
}
|
||||
|
||||
- ChunkStorage.injectDatafixingContext(nbt, worldKey, generatorCodecKey);
|
||||
- nbt = DataFixTypes.CHUNK.updateToCurrentVersion(this.fixerUpper, nbt, Math.max(1493, i));
|
||||
- ChunkStorage.removeDatafixingContext(nbt);
|
||||
- NbtUtils.addCurrentDataVersion(nbt);
|
||||
- return nbt;
|
||||
+ // 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 (i <= 2730 && !belowZeroGenerationInExistingChunks) {
|
||||
+ stopBelowZero = "full".equals(nbttagcompound.getCompound("Level").getString("Status"));
|
||||
+ }
|
||||
+ // Spigot end
|
||||
+
|
||||
+ ChunkStorage.injectDatafixingContext(nbttagcompound, resourcekey, optional);
|
||||
+ nbttagcompound = DataFixTypes.CHUNK.updateToCurrentVersion(this.fixerUpper, nbttagcompound, Math.max(1493, i));
|
||||
+ // Spigot start
|
||||
+ if (stopBelowZero) {
|
||||
+ nbttagcompound.putString("Status", net.minecraft.core.registries.BuiltInRegistries.CHUNK_STATUS.getKey(ChunkStatus.SPAWN).toString());
|
||||
+ }
|
||||
+ // Spigot end
|
||||
+ ChunkStorage.removeDatafixingContext(nbttagcompound);
|
||||
+ NbtUtils.addCurrentDataVersion(nbttagcompound);
|
||||
+ return nbttagcompound;
|
||||
} catch (Exception exception) {
|
||||
CrashReport crashreport = CrashReport.forThrowable(exception, "Updated chunk");
|
||||
CrashReportCategory crashreportsystemdetails = crashreport.addCategory("Updated chunk details");
|
||||
@@ -70,7 +135,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
- private LegacyStructureDataHandler getLegacyStructureHandler(ResourceKey<Level> worldKey, Supplier<DimensionDataStorage> stateManagerGetter) {
|
||||
+ private LegacyStructureDataHandler getLegacyStructureHandler(ResourceKey<LevelStem> worldKey, Supplier<DimensionDataStorage> stateManagerGetter) { // CraftBukkit
|
||||
LegacyStructureDataHandler persistentstructurelegacy = this.legacyStructureHandler;
|
||||
|
||||
if (persistentstructurelegacy == null) {
|
||||
@@ -85,7 +150,7 @@
|
||||
return persistentstructurelegacy;
|
||||
}
|
||||
|
||||
- public static void injectDatafixingContext(CompoundTag nbt, ResourceKey<Level> worldKey, Optional<ResourceKey<MapCodec<? extends ChunkGenerator>>> generatorCodecKey) {
|
||||
+ public static void injectDatafixingContext(CompoundTag nbt, ResourceKey<LevelStem> worldKey, Optional<ResourceKey<MapCodec<? extends ChunkGenerator>>> generatorCodecKey) { // CraftBukkit
|
||||
CompoundTag nbttagcompound1 = new CompoundTag();
|
||||
|
||||
nbttagcompound1.putString("dimension", worldKey.location().toString());
|
||||
@@ -108,8 +173,19 @@
|
||||
}
|
||||
|
||||
public CompletableFuture<Void> write(ChunkPos chunkPos, Supplier<CompoundTag> nbtSupplier) {
|
||||
+ // Paper start - guard against possible chunk pos desync
|
||||
+ final Supplier<CompoundTag> guardedPosCheck = () -> {
|
||||
+ CompoundTag nbt = nbtSupplier.get();
|
||||
+ if (nbt != null && !chunkPos.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 " + chunkPos
|
||||
+ + " 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(chunkPos);
|
||||
- return this.worker.store(chunkPos, nbtSupplier);
|
||||
+ return this.worker.store(chunkPos, guardedPosCheck); // Paper - guard against possible chunk pos desync
|
||||
}
|
||||
|
||||
protected void handleLegacyStructureIndex(ChunkPos chunkPos) {
|
|
@ -1,127 +0,0 @@
|
|||
--- a/net/minecraft/world/level/chunk/storage/RegionFile.java
|
||||
+++ b/net/minecraft/world/level/chunk/storage/RegionFile.java
|
||||
@@ -1,3 +1,4 @@
|
||||
+// mc-dev import
|
||||
package net.minecraft.world.level.chunk.storage;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
@@ -49,7 +50,7 @@
|
||||
protected final RegionBitmap usedSectors;
|
||||
|
||||
public RegionFile(RegionStorageInfo storageKey, Path directory, Path path, boolean dsync) throws IOException {
|
||||
- this(storageKey, directory, path, RegionFileVersion.getSelected(), dsync);
|
||||
+ this(storageKey, directory, path, RegionFileVersion.getCompressionFormat(), dsync); // Paper - Configurable region compression format
|
||||
}
|
||||
|
||||
public RegionFile(RegionStorageInfo storageKey, Path path, Path directory, RegionFileVersion compressionFormat, boolean dsync) throws IOException {
|
||||
@@ -63,8 +64,8 @@
|
||||
} else {
|
||||
this.externalFileDir = directory;
|
||||
this.offsets = this.header.asIntBuffer();
|
||||
- this.offsets.limit(1024);
|
||||
- this.header.position(4096);
|
||||
+ ((java.nio.Buffer) this.offsets).limit(1024); // CraftBukkit - decompile error
|
||||
+ ((java.nio.Buffer) this.header).position(4096); // CraftBukkit - decompile error
|
||||
this.timestamps = this.header.asIntBuffer();
|
||||
if (dsync) {
|
||||
this.file = FileChannel.open(path, StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.DSYNC);
|
||||
@@ -73,7 +74,7 @@
|
||||
}
|
||||
|
||||
this.usedSectors.force(0, 2);
|
||||
- this.header.position(0);
|
||||
+ ((java.nio.Buffer) this.header).position(0); // CraftBukkit - decompile error
|
||||
int i = this.file.read(this.header, 0L);
|
||||
|
||||
if (i != -1) {
|
||||
@@ -89,6 +90,14 @@
|
||||
if (l != 0) {
|
||||
int i1 = RegionFile.getSectorNumber(l);
|
||||
int j1 = RegionFile.getNumSectors(l);
|
||||
+ // Spigot start
|
||||
+ if (j1 == 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, i1 * 4096);
|
||||
+ j1 = (realLen.getInt(0) + 4) / 4096 + 1;
|
||||
+ }
|
||||
+ // Spigot end
|
||||
|
||||
if (i1 < 2) {
|
||||
RegionFile.LOGGER.warn("Region file {} has invalid sector at index: {}; sector {} overlaps with header", new Object[]{path, k, i1});
|
||||
@@ -128,11 +137,18 @@
|
||||
} else {
|
||||
int j = RegionFile.getSectorNumber(i);
|
||||
int k = RegionFile.getNumSectors(i);
|
||||
+ // Spigot start
|
||||
+ if (k == 255) {
|
||||
+ ByteBuffer realLen = ByteBuffer.allocate(4);
|
||||
+ this.file.read(realLen, j * 4096);
|
||||
+ k = (realLen.getInt(0) + 4) / 4096 + 1;
|
||||
+ }
|
||||
+ // Spigot end
|
||||
int l = k * 4096;
|
||||
ByteBuffer bytebuffer = ByteBuffer.allocate(l);
|
||||
|
||||
this.file.read(bytebuffer, (long) (j * 4096));
|
||||
- bytebuffer.flip();
|
||||
+ ((java.nio.Buffer) bytebuffer).flip(); // CraftBukkit - decompile error
|
||||
if (bytebuffer.remaining() < 5) {
|
||||
RegionFile.LOGGER.error("Chunk {} header is truncated: expected {} but read {}", new Object[]{pos, l, bytebuffer.remaining()});
|
||||
return null;
|
||||
@@ -246,7 +262,7 @@
|
||||
|
||||
try {
|
||||
this.file.read(bytebuffer, (long) (j * 4096));
|
||||
- bytebuffer.flip();
|
||||
+ ((java.nio.Buffer) bytebuffer).flip(); // CraftBukkit - decompile error
|
||||
if (bytebuffer.remaining() != 5) {
|
||||
return false;
|
||||
} else {
|
||||
@@ -280,6 +296,7 @@
|
||||
return true;
|
||||
}
|
||||
} catch (IOException ioexception) {
|
||||
+ com.destroystokyo.paper.exception.ServerInternalException.reportInternalException(ioexception); // Paper - ServerExceptionEvent
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -349,7 +366,7 @@
|
||||
|
||||
bytebuffer.putInt(1);
|
||||
bytebuffer.put((byte) (this.version.getId() | 128));
|
||||
- bytebuffer.flip();
|
||||
+ ((java.nio.Buffer) bytebuffer).flip(); // CraftBukkit - decompile error
|
||||
return bytebuffer;
|
||||
}
|
||||
|
||||
@@ -358,9 +375,10 @@
|
||||
FileChannel filechannel = FileChannel.open(path1, StandardOpenOption.CREATE, StandardOpenOption.WRITE);
|
||||
|
||||
try {
|
||||
- buf.position(5);
|
||||
+ ((java.nio.Buffer) buf).position(5); // CraftBukkit - decompile error
|
||||
filechannel.write(buf);
|
||||
} catch (Throwable throwable) {
|
||||
+ com.destroystokyo.paper.exception.ServerInternalException.reportInternalException(throwable); // Paper - ServerExceptionEvent
|
||||
if (filechannel != null) {
|
||||
try {
|
||||
filechannel.close();
|
||||
@@ -382,7 +400,7 @@
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -418,7 +436,7 @@
|
||||
if (i != j) {
|
||||
ByteBuffer bytebuffer = RegionFile.PADDING_BUFFER.duplicate();
|
||||
|
||||
- bytebuffer.position(0);
|
||||
+ ((java.nio.Buffer) bytebuffer).position(0); // CraftBukkit - decompile error
|
||||
this.file.write(bytebuffer, (long) (j - 1));
|
||||
}
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
--- a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
|
||||
+++ b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
|
||||
@@ -32,21 +32,22 @@
|
||||
this.info = storageKey;
|
||||
}
|
||||
|
||||
- private RegionFile getRegionFile(ChunkPos pos) throws IOException {
|
||||
- long i = ChunkPos.asLong(pos.getRegionX(), pos.getRegionZ());
|
||||
+ private RegionFile getRegionFile(ChunkPos chunkcoordintpair, boolean existingOnly) throws IOException { // CraftBukkit
|
||||
+ long i = ChunkPos.asLong(chunkcoordintpair.getRegionX(), chunkcoordintpair.getRegionZ());
|
||||
RegionFile regionfile = (RegionFile) this.regionCache.getAndMoveToFirst(i);
|
||||
|
||||
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
|
||||
((RegionFile) this.regionCache.removeLast()).close();
|
||||
}
|
||||
|
||||
FileUtil.createDirectoriesSafe(this.folder);
|
||||
Path path = this.folder;
|
||||
- int j = pos.getRegionX();
|
||||
- Path path1 = path.resolve("r." + j + "." + pos.getRegionZ() + ".mca");
|
||||
+ int j = chunkcoordintpair.getRegionX();
|
||||
+ Path path1 = path.resolve("r." + j + "." + chunkcoordintpair.getRegionZ() + ".mca");
|
||||
+ if (existingOnly && !java.nio.file.Files.exists(path1)) return null; // CraftBukkit
|
||||
RegionFile regionfile1 = new RegionFile(this.info, path1, this.folder, this.sync);
|
||||
|
||||
this.regionCache.putAndMoveToFirst(i, regionfile1);
|
||||
@@ -56,7 +57,12 @@
|
||||
|
||||
@Nullable
|
||||
public CompoundTag read(ChunkPos pos) throws IOException {
|
||||
- RegionFile regionfile = this.getRegionFile(pos);
|
||||
+ // CraftBukkit start - SPIGOT-5680: There's no good reason to preemptively create files on read, save that for writing
|
||||
+ RegionFile regionfile = this.getRegionFile(pos, true);
|
||||
+ if (regionfile == null) {
|
||||
+ return null;
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
DataInputStream datainputstream = regionfile.getChunkDataInputStream(pos);
|
||||
|
||||
CompoundTag nbttagcompound;
|
||||
@@ -96,7 +102,12 @@
|
||||
}
|
||||
|
||||
public void scanChunk(ChunkPos chunkPos, StreamTagVisitor scanner) 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
|
||||
DataInputStream datainputstream = regionfile.getChunkDataInputStream(chunkPos);
|
||||
|
||||
try {
|
||||
@@ -122,7 +133,7 @@
|
||||
}
|
||||
|
||||
protected void write(ChunkPos pos, @Nullable CompoundTag nbt) throws IOException {
|
||||
- RegionFile regionfile = this.getRegionFile(pos);
|
||||
+ RegionFile regionfile = this.getRegionFile(pos, false); // CraftBukkit
|
||||
|
||||
if (nbt == null) {
|
||||
regionfile.clear(pos);
|
|
@ -1,216 +0,0 @@
|
|||
--- a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
|
||||
+++ b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
|
||||
@@ -76,7 +76,8 @@
|
||||
import net.minecraft.world.ticks.SavedTick;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
-public record SerializableChunkData(Registry<Biome> biomeRegistry, ChunkPos chunkPos, int minSectionY, long lastUpdateTime, long inhabitedTime, ChunkStatus chunkStatus, @Nullable BlendingData.Packed blendingData, @Nullable BelowZeroRetrogen belowZeroRetrogen, UpgradeData upgradeData, @Nullable long[] carvingMask, Map<Heightmap.Types, long[]> heightmaps, ChunkAccess.PackedTicks packedTicks, ShortList[] postProcessingSections, boolean lightCorrect, List<SerializableChunkData.SectionData> sectionData, List<CompoundTag> entities, List<CompoundTag> blockEntities, CompoundTag structureData) {
|
||||
+// CraftBukkit - persistentDataContainer
|
||||
+public record SerializableChunkData(Registry<Biome> biomeRegistry, ChunkPos chunkPos, int minSectionY, long lastUpdateTime, long inhabitedTime, ChunkStatus chunkStatus, @Nullable BlendingData.Packed blendingData, @Nullable BelowZeroRetrogen belowZeroRetrogen, UpgradeData upgradeData, @Nullable long[] carvingMask, Map<Heightmap.Types, long[]> heightmaps, ChunkAccess.PackedTicks packedTicks, ShortList[] postProcessingSections, boolean lightCorrect, List<SerializableChunkData.SectionData> sectionData, List<CompoundTag> entities, List<CompoundTag> blockEntities, CompoundTag structureData, @Nullable Tag 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());
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
@@ -90,13 +91,39 @@
|
||||
public static final String SECTIONS_TAG = "sections";
|
||||
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 world, RegistryAccess registryManager, CompoundTag nbt) {
|
||||
if (!nbt.contains("Status", 8)) {
|
||||
return null;
|
||||
} else {
|
||||
- ChunkPos chunkcoordintpair = new ChunkPos(nbt.getInt("xPos"), nbt.getInt("zPos"));
|
||||
+ // Paper start - Do not let the server load chunks from newer versions
|
||||
+ if (nbt.contains("DataVersion", net.minecraft.nbt.Tag.TAG_ANY_NUMERIC)) {
|
||||
+ final int dataVersion = nbt.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 chunkcoordintpair = new ChunkPos(nbt.getInt("xPos"), nbt.getInt("zPos")); // Paper - guard against serializing mismatching coordinates; diff on change, see ChunkSerializer#getChunkCoordinate
|
||||
long i = nbt.getLong("LastUpdate");
|
||||
long j = nbt.getLong("InhabitedTime");
|
||||
ChunkStatus chunkstatus = ChunkStatus.byName(nbt.getString("Status"));
|
||||
@@ -110,7 +137,7 @@
|
||||
dataresult = BlendingData.Packed.CODEC.parse(NbtOps.INSTANCE, nbt.getCompound("blending_data"));
|
||||
logger = SerializableChunkData.LOGGER;
|
||||
Objects.requireNonNull(logger);
|
||||
- blendingdata_d = (BlendingData.Packed) dataresult.resultOrPartial(logger::error).orElse((Object) null);
|
||||
+ blendingdata_d = (BlendingData.Packed) ((DataResult<BlendingData.Packed>) dataresult).resultOrPartial(logger::error).orElse(null); // CraftBukkit - decompile error
|
||||
} else {
|
||||
blendingdata_d = null;
|
||||
}
|
||||
@@ -121,7 +148,7 @@
|
||||
dataresult = BelowZeroRetrogen.CODEC.parse(NbtOps.INSTANCE, nbt.getCompound("below_zero_retrogen"));
|
||||
logger = SerializableChunkData.LOGGER;
|
||||
Objects.requireNonNull(logger);
|
||||
- belowzeroretrogen = (BelowZeroRetrogen) dataresult.resultOrPartial(logger::error).orElse((Object) null);
|
||||
+ belowzeroretrogen = (BelowZeroRetrogen) ((DataResult<BelowZeroRetrogen>) dataresult).resultOrPartial(logger::error).orElse(null); // CraftBukkit - decompile error
|
||||
} else {
|
||||
belowzeroretrogen = null;
|
||||
}
|
||||
@@ -178,7 +205,7 @@
|
||||
ListTag nbttaglist2 = nbt.getList("sections", 10);
|
||||
List<SerializableChunkData.SectionData> list4 = new ArrayList(nbttaglist2.size());
|
||||
Registry<Biome> iregistry = registryManager.lookupOrThrow(Registries.BIOME);
|
||||
- Codec<PalettedContainerRO<Holder<Biome>>> codec = makeBiomeCodec(iregistry);
|
||||
+ Codec<PalettedContainer<Holder<Biome>>> codec = makeBiomeCodecRW(iregistry); // CraftBukkit - read/write
|
||||
|
||||
for (int i1 = 0; i1 < nbttaglist2.size(); ++i1) {
|
||||
CompoundTag nbttagcompound3 = nbttaglist2.getCompound(i1);
|
||||
@@ -196,17 +223,17 @@
|
||||
datapaletteblock = new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES);
|
||||
}
|
||||
|
||||
- Object object;
|
||||
+ PalettedContainer object; // CraftBukkit - read/write
|
||||
|
||||
if (nbttagcompound3.contains("biomes", 10)) {
|
||||
- object = (PalettedContainerRO) codec.parse(NbtOps.INSTANCE, nbttagcompound3.getCompound("biomes")).promotePartial((s1) -> {
|
||||
+ object = codec.parse(NbtOps.INSTANCE, nbttagcompound3.getCompound("biomes")).promotePartial((s1) -> { // CraftBukkit - read/write
|
||||
logErrors(chunkcoordintpair, b0, s1);
|
||||
}).getOrThrow(SerializableChunkData.ChunkReadException::new);
|
||||
} else {
|
||||
object = new PalettedContainer<>(iregistry.asHolderIdMap(), iregistry.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
|
||||
}
|
||||
|
||||
- chunksection = new LevelChunkSection(datapaletteblock, (PalettedContainerRO) object);
|
||||
+ chunksection = new LevelChunkSection(datapaletteblock, (PalettedContainer) object); // CraftBukkit - read/write
|
||||
} else {
|
||||
chunksection = null;
|
||||
}
|
||||
@@ -217,7 +244,8 @@
|
||||
list4.add(new SerializableChunkData.SectionData(b0, chunksection, nibblearray, nibblearray1));
|
||||
}
|
||||
|
||||
- return new SerializableChunkData(iregistry, chunkcoordintpair, world.getMinSectionY(), i, j, chunkstatus, blendingdata_d, belowzeroretrogen, chunkconverter, along, map, ichunkaccess_a, ashortlist, flag, list4, list2, list3, nbttagcompound2);
|
||||
+ // CraftBukkit - ChunkBukkitValues
|
||||
+ return new SerializableChunkData(iregistry, chunkcoordintpair, world.getMinSectionY(), i, j, chunkstatus, blendingdata_d, belowzeroretrogen, chunkconverter, along, map, ichunkaccess_a, ashortlist, flag, list4, list2, list3, nbttagcompound2, nbt.get("ChunkBukkitValues"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,7 +315,13 @@
|
||||
if (this.chunkStatus.isOrAfter(ChunkStatus.INITIALIZE_LIGHT)) {
|
||||
protochunk.setLightEngine(levellightengine);
|
||||
}
|
||||
+ }
|
||||
+
|
||||
+ // 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) object).persistentDataContainer.putAll((CompoundTag) this.persistentDataContainer);
|
||||
}
|
||||
+ // CraftBukkit end
|
||||
|
||||
((ChunkAccess) object).setLightCorrect(this.lightCorrect);
|
||||
EnumSet<Heightmap.Types> enumset = EnumSet.noneOf(Heightmap.Types.class);
|
||||
@@ -329,6 +363,13 @@
|
||||
|
||||
while (iterator2.hasNext()) {
|
||||
nbttagcompound = (CompoundTag) iterator2.next();
|
||||
+ // Paper start - do not read tile entities positioned outside the chunk
|
||||
+ final BlockPos blockposition = BlockEntity.getPosFromTag(nbttagcompound);
|
||||
+ if ((blockposition.getX() >> 4) != chunkPos.x || (blockposition.getZ() >> 4) != chunkPos.z) {
|
||||
+ LOGGER.warn("Tile entity serialized in chunk {} in world '{}' positioned at {} is located outside of the chunk", chunkPos, world.getWorld().getName(), blockposition);
|
||||
+ continue;
|
||||
+ }
|
||||
+ // Paper end - do not read tile entities positioned outside the chunk
|
||||
protochunk1.setBlockEntityNbt(nbttagcompound);
|
||||
}
|
||||
|
||||
@@ -348,6 +389,12 @@
|
||||
return PalettedContainer.codecRO(biomeRegistry.asHolderIdMap(), biomeRegistry.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomeRegistry.getOrThrow(Biomes.PLAINS));
|
||||
}
|
||||
|
||||
+ // 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 world, ChunkAccess chunk) {
|
||||
if (!chunk.canBeSerialized()) {
|
||||
throw new IllegalArgumentException("Chunk can't be serialized: " + String.valueOf(chunk));
|
||||
@@ -419,7 +466,14 @@
|
||||
});
|
||||
CompoundTag nbttagcompound1 = packStructureData(StructurePieceSerializationContext.fromLevel(world), chunkcoordintpair, chunk.getAllStarts(), chunk.getAllReferences());
|
||||
|
||||
- return new SerializableChunkData(world.registryAccess().lookupOrThrow(Registries.BIOME), chunkcoordintpair, chunk.getMinSectionY(), world.getGameTime(), chunk.getInhabitedTime(), chunk.getPersistedStatus(), (BlendingData.Packed) Optionull.map(chunk.getBlendingData(), BlendingData::pack), chunk.getBelowZeroRetrogen(), chunk.getUpgradeData().copy(), along, map, ichunkaccess_a, ashortlist, chunk.isLightCorrect(), list, list2, list1, nbttagcompound1);
|
||||
+ // 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();
|
||||
+ }
|
||||
+
|
||||
+ return new SerializableChunkData(world.registryAccess().lookupOrThrow(Registries.BIOME), chunkcoordintpair, chunk.getMinSectionY(), world.getGameTime(), chunk.getInhabitedTime(), chunk.getPersistedStatus(), (BlendingData.Packed) Optionull.map(chunk.getBlendingData(), BlendingData::pack), chunk.getBelowZeroRetrogen(), chunk.getUpgradeData().copy(), along, map, ichunkaccess_a, ashortlist, chunk.isLightCorrect(), list, list2, list1, nbttagcompound1, persistentDataContainer);
|
||||
+ // CraftBukkit end
|
||||
}
|
||||
}
|
||||
|
||||
@@ -432,7 +486,7 @@
|
||||
nbttagcompound.putLong("LastUpdate", this.lastUpdateTime);
|
||||
nbttagcompound.putLong("InhabitedTime", this.inhabitedTime);
|
||||
nbttagcompound.putString("Status", BuiltInRegistries.CHUNK_STATUS.getKey(this.chunkStatus).toString());
|
||||
- DataResult dataresult;
|
||||
+ DataResult<Tag> dataresult; // CraftBukkit - decompile error
|
||||
Logger logger;
|
||||
|
||||
if (this.blendingData != null) {
|
||||
@@ -513,6 +567,11 @@
|
||||
});
|
||||
nbttagcompound.put("Heightmaps", nbttagcompound2);
|
||||
nbttagcompound.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.
|
||||
+ nbttagcompound.put("ChunkBukkitValues", this.persistentDataContainer);
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
return nbttagcompound;
|
||||
}
|
||||
|
||||
@@ -564,6 +623,13 @@
|
||||
chunk.setBlockEntityNbt(nbttagcompound);
|
||||
} else {
|
||||
BlockPos blockposition = BlockEntity.getPosFromTag(nbttagcompound);
|
||||
+ // Paper start - do not read tile entities positioned outside the chunk
|
||||
+ ChunkPos chunkPos = chunk.getPos();
|
||||
+ if ((blockposition.getX() >> 4) != chunkPos.x || (blockposition.getZ() >> 4) != chunkPos.z) {
|
||||
+ LOGGER.warn("Tile entity serialized in chunk " + chunkPos + " in world '" + world.getWorld().getName() + "' positioned at " + blockposition + " is located outside of the chunk");
|
||||
+ continue;
|
||||
+ }
|
||||
+ // Paper end - do not read tile entities positioned outside the chunk
|
||||
BlockEntity tileentity = BlockEntity.loadStatic(blockposition, chunk.getBlockState(blockposition), nbttagcompound, world.registryAccess());
|
||||
|
||||
if (tileentity != null) {
|
||||
@@ -623,6 +689,12 @@
|
||||
StructureStart structurestart = StructureStart.loadStaticStart(context, nbttagcompound1.getCompound(s), worldSeed);
|
||||
|
||||
if (structurestart != null) {
|
||||
+ // CraftBukkit start - load persistent data for structure start
|
||||
+ net.minecraft.nbt.Tag persistentBase = nbttagcompound1.getCompound(s).get("StructureBukkitValues");
|
||||
+ if (persistentBase instanceof CompoundTag) {
|
||||
+ structurestart.persistentDataContainer.putAll((CompoundTag) persistentBase);
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
map.put(structure, structurestart);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue