Update remaining feature patches

This commit is contained in:
Nassim Jahnke 2024-12-20 23:02:39 +01:00
parent b0cef6818d
commit c45286cb08
No known key found for this signature in database
GPG key ID: EF6771C01F6EF02F
4 changed files with 329 additions and 353 deletions

View file

@ -7,22 +7,23 @@ Subject: [PATCH] Only write chunk data to disk if it serializes without
This ensures at least a valid version of the chunk exists This ensures at least a valid version of the chunk exists
on disk, even if outdated on disk, even if outdated
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 diff --git a/net/minecraft/world/level/chunk/storage/RegionFile.java b/net/minecraft/world/level/chunk/storage/RegionFile.java
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java index 7da388ffab162c282cad0f297bb7304f3c2abbaf..ff4fc280409f680f3879a495e37cf1925b1a38f1 100644
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java --- a/net/minecraft/world/level/chunk/storage/RegionFile.java
@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche +++ b/net/minecraft/world/level/chunk/storage/RegionFile.java
@@ -24,6 +24,7 @@ import org.slf4j.Logger;
} public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patches.chunk_system.storage.ChunkSystemRegionFile { // Paper - rewrite chunk system
// Paper end private static final Logger LOGGER = LogUtils.getLogger();
+ public static final int MAX_CHUNK_SIZE = 500 * 1024 * 1024; // Paper - don't write garbage data to disk if writing serialization fails + public static final int MAX_CHUNK_SIZE = 500 * 1024 * 1024; // Paper - don't write garbage data to disk if writing serialization fails
private class ChunkBuffer extends ByteArrayOutputStream implements ca.spottedleaf.moonrise.patches.chunk_system.storage.ChunkSystemChunkBuffer { // Paper - rewrite chunk system private static final int SECTOR_BYTES = 4096;
@VisibleForTesting
private final ChunkPos pos; protected static final int SECTOR_INTS = 1024;
@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche @@ -455,6 +456,24 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
super.write(RegionFile.this.version.getId()); this.pos = pos;
this.pos = chunkcoordintpair;
} }
+ // Paper start - don't write garbage data to disk if writing serialization fails + // Paper start - don't write garbage data to disk if writing serialization fails
+ @Override + @Override
+ public void write(final int b) { + public void write(final int b) {
@ -40,23 +41,23 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ super.write(b, off, len); + super.write(b, off, len);
+ } + }
+ // Paper end - don't write garbage data to disk if writing serialization fails + // Paper end - don't write garbage data to disk if writing serialization fails
+
@Override
public void close() throws IOException { public void close() throws IOException {
ByteBuffer bytebuffer = ByteBuffer.wrap(this.buf, 0, this.count); ByteBuffer byteBuffer = ByteBuffer.wrap(this.buf, 0, this.count);
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java diff --git a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 index e35bb5534e2fbd2e30154a15ff6d39baa121608f..d263f78fa610ce6f6fb5a0f5e064e3d8335c2199 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java --- a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java +++ b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
@@ -0,0 +0,0 @@ import net.minecraft.world.level.ChunkPos; @@ -15,6 +15,7 @@ import net.minecraft.util.ExceptionCollector;
import net.minecraft.world.level.ChunkPos;
public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise.patches.chunk_system.io.ChunkSystemRegionFileStorage { // Paper - rewrite chunk system public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise.patches.chunk_system.io.ChunkSystemRegionFileStorage { // Paper - rewrite chunk system
+ private static final org.slf4j.Logger LOGGER = com.mojang.logging.LogUtils.getLogger(); // Paper + private static final org.slf4j.Logger LOGGER = com.mojang.logging.LogUtils.getLogger(); // Paper
+
public static final String ANVIL_EXTENSION = ".mca"; public static final String ANVIL_EXTENSION = ".mca";
private static final int MAX_CACHE_SIZE = 256; private static final int MAX_CACHE_SIZE = 256;
public final Long2ObjectLinkedOpenHashMap<RegionFile> regionCache = new Long2ObjectLinkedOpenHashMap(); public final Long2ObjectLinkedOpenHashMap<RegionFile> regionCache = new Long2ObjectLinkedOpenHashMap<>();
@@ -0,0 +0,0 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise @@ -119,11 +120,24 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
// (and, the regionfile parameter is unused for writing until the write call) // (and, the regionfile parameter is unused for writing until the write call)
final ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.WriteData writeData = ((ca.spottedleaf.moonrise.patches.chunk_system.storage.ChunkSystemRegionFile)regionFile).moonrise$startWrite(compound, pos); final ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.WriteData writeData = ((ca.spottedleaf.moonrise.patches.chunk_system.storage.ChunkSystemRegionFile)regionFile).moonrise$startWrite(compound, pos);
@ -81,39 +82,26 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
return writeData; return writeData;
} }
@@ -0,0 +0,0 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise @@ -326,9 +340,17 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
try { if (chunkData == null) {
NbtIo.write(nbt, (DataOutput) dataoutputstream); regionFile.clear(chunkPos);
regionfile.setOversized(pos.x, pos.z, false); // Paper - We don't do this anymore, mojang stores differently, but clear old meta flag if it exists to get rid of our own meta file once last oversized is gone } else {
- try (DataOutputStream chunkDataOutputStream = regionFile.getChunkDataOutputStream(chunkPos)) {
+ DataOutputStream chunkDataOutputStream = regionFile.getChunkDataOutputStream(chunkPos); // Paper - Only write if successful
+ try { // Paper - Only write if successful
NbtIo.write(chunkData, chunkDataOutputStream);
regionFile.setOversized(chunkPos.x, chunkPos.z, false); // Paper - We don't do this anymore, mojang stores differently, but clear old meta flag if it exists to get rid of our own meta file once last oversized is gone
+ // Paper start - don't write garbage data to disk if writing serialization fails + // Paper start - don't write garbage data to disk if writing serialization fails
+ dataoutputstream.close(); // Only write if successful + chunkDataOutputStream.close();
+ } catch (final RegionFileSizeException ex) { + } catch (final RegionFileSizeException ex) {
+ regionfile.clear(pos); + regionFile.clear(chunkPos);
+ final int maxSize = RegionFile.MAX_CHUNK_SIZE / (1024 * 1024); + final int maxSize = RegionFile.MAX_CHUNK_SIZE / (1024 * 1024);
+ LOGGER.error("Chunk at (" + pos.x + "," + pos.z + ") in regionfile '" + regionfile.getPath().toString() + "' exceeds max size of " + maxSize + "MiB, it has been deleted from disk."); + LOGGER.error("Chunk at (" + chunkPos.x + "," + chunkPos.z + ") in regionfile '" + regionFile.getPath().toString() + "' exceeds max size of " + maxSize + "MiB, it has been deleted from disk.");
+ return;
+ // Paper end - don't write garbage data to disk if writing serialization fails + // Paper end - don't write garbage data to disk if writing serialization fails
} catch (Throwable throwable) {
if (dataoutputstream != null) {
try {
- dataoutputstream.close();
+ //dataoutputstream.close(); // Paper - don't write garbage data to disk if writing serialization fails
} catch (Throwable throwable1) {
throwable.addSuppressed(throwable1);
}
@@ -0,0 +0,0 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
throw throwable;
} }
-
- if (dataoutputstream != null) {
- dataoutputstream.close();
- }
+ // Paper - don't write garbage data to disk if writing serialization fails; move into try block to only write if successfully serialized
} }
} }
@@ -0,0 +0,0 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise @@ -370,4 +392,13 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
public RegionStorageInfo info() { public RegionStorageInfo info() {
return this.info; return this.info;
} }
@ -121,7 +109,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ // Paper start - don't write garbage data to disk if writing serialization fails + // Paper start - don't write garbage data to disk if writing serialization fails
+ public static final class RegionFileSizeException extends RuntimeException { + public static final class RegionFileSizeException extends RuntimeException {
+ +
+ public RegionFileSizeException(String message) { + public RegionFileSizeException(final String message) {
+ super(message); + super(message);
+ } + }
+ } + }

View file

@ -8,11 +8,12 @@ to a chunk. The default values of -1 disable the limit. Although
defaults are only included for certain entites, this allows setting defaults are only included for certain entites, this allows setting
limits for any entity type. limits for any entity type.
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java
--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java index 7aea4e343581b977d11af90f9f65eac3532eade1..d21ce54ebb5724c04eadf56a2cde701d5eeb5db2 100644
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java --- a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java
@@ -0,0 +0,0 @@ public final class ChunkEntitySlices { +++ b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java
@@ -104,7 +104,18 @@ public final class ChunkEntitySlices {
} }
final ListTag entitiesTag = new ListTag(); final ListTag entitiesTag = new ListTag();
@ -31,21 +32,21 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
CompoundTag compoundTag = new CompoundTag(); CompoundTag compoundTag = new CompoundTag();
if (entity.save(compoundTag)) { if (entity.save(compoundTag)) {
entitiesTag.add(compoundTag); entitiesTag.add(compoundTag);
diff --git a/src/main/java/net/minecraft/world/entity/EntityType.java b/src/main/java/net/minecraft/world/entity/EntityType.java diff --git a/net/minecraft/world/entity/EntityType.java b/net/minecraft/world/entity/EntityType.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 index 73cdfa5a315ed259b38dfa946a0b7955d9ac9f50..49201d6664656ebe34c84c1c84b5ea4878729062 100644
--- a/src/main/java/net/minecraft/world/entity/EntityType.java --- a/net/minecraft/world/entity/EntityType.java
+++ b/src/main/java/net/minecraft/world/entity/EntityType.java +++ b/net/minecraft/world/entity/EntityType.java
@@ -0,0 +0,0 @@ public class EntityType<T extends Entity> implements FeatureElement, EntityTypeT @@ -1420,9 +1420,20 @@ public class EntityType<T extends Entity> implements FeatureElement, EntityTypeT
final Spliterator<? extends Tag> spliterator = entityNbtList.spliterator(); public static Stream<Entity> loadEntitiesRecursive(final List<? extends Tag> entityTags, final Level level, final EntitySpawnReason spawnReason) {
final Spliterator<? extends Tag> spliterator = entityTags.spliterator();
return StreamSupport.stream(new Spliterator<Entity>() { return StreamSupport.stream(new Spliterator<Entity>() {
+ final java.util.Map<EntityType<?>, Integer> loadedEntityCounts = new java.util.HashMap<>(); // Paper - Entity load/save limit per chunk + final java.util.Map<EntityType<?>, Integer> loadedEntityCounts = new java.util.HashMap<>(); // Paper - Entity load/save limit per chunk
@Override
public boolean tryAdvance(Consumer<? super Entity> consumer) { public boolean tryAdvance(Consumer<? super Entity> consumer) {
return spliterator.tryAdvance((nbtbase) -> { return spliterator.tryAdvance(tag -> EntityType.loadEntityRecursive((CompoundTag)tag, level, spawnReason, entity -> {
EntityType.loadEntityRecursive((CompoundTag) nbtbase, world, reason, (entity) -> {
+ // Paper start - Entity load/save limit per chunk + // Paper start - Entity load/save limit per chunk
+ final EntityType<?> entityType = entity.getType(); + final EntityType<?> entityType = entity.getType();
+ final int saveLimit = world.paperConfig().chunks.entityPerChunkSaveLimit.getOrDefault(entityType, -1); + final int saveLimit = level.paperConfig().chunks.entityPerChunkSaveLimit.getOrDefault(entityType, -1);
+ if (saveLimit > -1) { + if (saveLimit > -1) {
+ if (this.loadedEntityCounts.getOrDefault(entityType, 0) >= saveLimit) { + if (this.loadedEntityCounts.getOrDefault(entityType, 0) >= saveLimit) {
+ return null; + return null;
@ -53,19 +54,19 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ this.loadedEntityCounts.merge(entityType, 1, Integer::sum); + this.loadedEntityCounts.merge(entityType, 1, Integer::sum);
+ } + }
+ // Paper end - Entity load/save limit per chunk + // Paper end - Entity load/save limit per chunk
consumer.accept(entity); consumer.accept(entity);
return entity; return entity;
}); }));
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java diff --git a/net/minecraft/world/level/chunk/storage/EntityStorage.java b/net/minecraft/world/level/chunk/storage/EntityStorage.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 index da05fb780c55381a7a08ced51d01764a645740b2..2856206eafddfcbcc1b65408deda40357f43a6f8 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java --- a/net/minecraft/world/level/chunk/storage/EntityStorage.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java +++ b/net/minecraft/world/level/chunk/storage/EntityStorage.java
@@ -0,0 +0,0 @@ public class EntityStorage implements EntityPersistentStorage<Entity> { @@ -93,7 +93,18 @@ public class EntityStorage implements EntityPersistentStorage<Entity> {
} }
} else { } else {
ListTag listTag = new ListTag(); ListTag listTag = new ListTag();
+ final java.util.Map<net.minecraft.world.entity.EntityType<?>, Integer> savedEntityCounts = new java.util.HashMap<>(); // Paper - Entity load/save limit per chunk + final java.util.Map<net.minecraft.world.entity.EntityType<?>, Integer> savedEntityCounts = new java.util.HashMap<>(); // Paper - Entity load/save limit per chunk
dataList.getEntities().forEach(entity -> { entities.getEntities().forEach(entity -> {
+ // Paper start - Entity load/save limit per chunk + // Paper start - Entity load/save limit per chunk
+ final EntityType<?> entityType = entity.getType(); + final EntityType<?> entityType = entity.getType();
+ final int saveLimit = this.level.paperConfig().chunks.entityPerChunkSaveLimit.getOrDefault(entityType, -1); + final int saveLimit = this.level.paperConfig().chunks.entityPerChunkSaveLimit.getOrDefault(entityType, -1);
@ -76,6 +77,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ savedEntityCounts.merge(entityType, 1, Integer::sum); + savedEntityCounts.merge(entityType, 1, Integer::sum);
+ } + }
+ // Paper end - Entity load/save limit per chunk + // Paper end - Entity load/save limit per chunk
CompoundTag compoundTagx = new CompoundTag(); CompoundTag compoundTag1 = new CompoundTag();
if (entity.save(compoundTagx)) { if (entity.save(compoundTag1)) {
listTag.add(compoundTagx); listTag.add(compoundTag1);

View file

@ -9,11 +9,12 @@ we instead drop the current regionfile header and recalculate -
hoping that at least then we don't swap chunks, and maybe recover hoping that at least then we don't swap chunks, and maybe recover
them all. them all.
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionBitmap.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionBitmap.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 diff --git a/net/minecraft/world/level/chunk/storage/RegionBitmap.java b/net/minecraft/world/level/chunk/storage/RegionBitmap.java
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionBitmap.java index 64a718c98f799c62a5bb28e1e8e5f66cc96c915d..666f2e967c99f78422c83fb20e1a3bf3efa7845e 100644
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionBitmap.java --- a/net/minecraft/world/level/chunk/storage/RegionBitmap.java
@@ -0,0 +0,0 @@ import java.util.BitSet; +++ b/net/minecraft/world/level/chunk/storage/RegionBitmap.java
@@ -9,6 +9,27 @@ import java.util.BitSet;
public class RegionBitmap { public class RegionBitmap {
private final BitSet used = new BitSet(); private final BitSet used = new BitSet();
@ -38,17 +39,17 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ } + }
+ // Paper end - Attempt to recalculate regionfile header if it is corrupt + // Paper end - Attempt to recalculate regionfile header if it is corrupt
+ +
public void force(int start, int size) { public void force(int sectorOffset, int sectorCount) {
this.used.set(start, start + size); this.used.set(sectorOffset, sectorOffset + sectorCount);
} }
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java diff --git a/net/minecraft/world/level/chunk/storage/RegionFile.java b/net/minecraft/world/level/chunk/storage/RegionFile.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 index ff4fc280409f680f3879a495e37cf1925b1a38f1..a4621c96fd456c5cdd1b6847931806e677b26b30 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java --- a/net/minecraft/world/level/chunk/storage/RegionFile.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java +++ b/net/minecraft/world/level/chunk/storage/RegionFile.java
@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche @@ -46,6 +46,355 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
private final IntBuffer timestamps;
@VisibleForTesting @VisibleForTesting
protected final RegionBitmap usedSectors; protected final RegionBitmap usedSectors = new RegionBitmap();
+ // Paper start - Attempt to recalculate regionfile header if it is corrupt + // Paper start - Attempt to recalculate regionfile header if it is corrupt
+ private static long roundToSectors(long bytes) { + private static long roundToSectors(long bytes) {
+ long sectors = bytes >>> 12; // 4096 = 2^12 + long sectors = bytes >>> 12; // 4096 = 2^12
@ -57,9 +58,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ return sectors + (sign >>> 63); + return sectors + (sign >>> 63);
+ } + }
+ +
+ private static final CompoundTag OVERSIZED_COMPOUND = new CompoundTag(); + private static final net.minecraft.nbt.CompoundTag OVERSIZED_COMPOUND = new net.minecraft.nbt.CompoundTag();
+ +
+ private CompoundTag attemptRead(long sector, int chunkDataLength, long fileLength) throws IOException { + private @Nullable net.minecraft.nbt.CompoundTag attemptRead(long sector, int chunkDataLength, long fileLength) throws IOException {
+ try { + try {
+ if (chunkDataLength < 0) { + if (chunkDataLength < 0) {
+ return null; + return null;
@ -91,7 +92,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ +
+ InputStream input = compression.wrap(new ByteArrayInputStream(chunkData.array(), chunkData.position(), chunkDataLength - chunkData.position())); + InputStream input = compression.wrap(new ByteArrayInputStream(chunkData.array(), chunkData.position(), chunkDataLength - chunkData.position()));
+ +
+ return NbtIo.read(new DataInputStream(input)); + return net.minecraft.nbt.NbtIo.read(new DataInputStream(input));
+ } catch (Exception ex) { + } catch (Exception ex) {
+ return null; + return null;
+ } + }
@ -142,7 +143,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ // try to backup file so maybe it could be sent to us for further investigation + // try to backup file so maybe it could be sent to us for further investigation
+ +
+ this.backupRegionFile(); + this.backupRegionFile();
+ CompoundTag[] compounds = new CompoundTag[32 * 32]; // only in the regionfile (i.e exclude mojang/aikar oversized data) + net.minecraft.nbt.CompoundTag[] compounds = new net.minecraft.nbt.CompoundTag[32 * 32]; // only in the regionfile (i.e exclude mojang/aikar oversized data)
+ int[] rawLengths = new int[32 * 32]; // length of chunk data including 4 byte length field, bytes + int[] rawLengths = new int[32 * 32]; // length of chunk data including 4 byte length field, bytes
+ int[] sectorOffsets = new int[32 * 32]; // in sectors + int[] sectorOffsets = new int[32 * 32]; // in sectors
+ boolean[] hasAikarOversized = new boolean[32 * 32]; + boolean[] hasAikarOversized = new boolean[32 * 32];
@ -154,7 +155,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ +
+ for (long i = 2, maxSector = Math.min((long)(Integer.MAX_VALUE >>> 8), totalSectors); i < maxSector; ++i) { // first two sectors are header, skip + for (long i = 2, maxSector = Math.min((long)(Integer.MAX_VALUE >>> 8), totalSectors); i < maxSector; ++i) { // first two sectors are header, skip
+ int chunkDataLength = this.getLength(i); + int chunkDataLength = this.getLength(i);
+ CompoundTag compound = this.attemptRead(i, chunkDataLength, fileLength); + net.minecraft.nbt.CompoundTag compound = this.attemptRead(i, chunkDataLength, fileLength);
+ if (compound == null || compound == OVERSIZED_COMPOUND) { + if (compound == null || compound == OVERSIZED_COMPOUND) {
+ continue; + continue;
+ } + }
@ -166,7 +167,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ } + }
+ int location = (chunkPos.x & 31) | ((chunkPos.z & 31) << 5); + int location = (chunkPos.x & 31) | ((chunkPos.z & 31) << 5);
+ +
+ CompoundTag otherCompound = compounds[location]; + net.minecraft.nbt.CompoundTag otherCompound = compounds[location];
+ +
+ if (otherCompound != null && SerializableChunkData.getLastWorldSaveTime(otherCompound) > SerializableChunkData.getLastWorldSaveTime(compound)) { + if (otherCompound != null && SerializableChunkData.getLastWorldSaveTime(otherCompound) > SerializableChunkData.getLastWorldSaveTime(compound)) {
+ continue; // don't overwrite newer data. + continue; // don't overwrite newer data.
@ -177,7 +178,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ boolean isAikarOversized = false; + boolean isAikarOversized = false;
+ if (Files.exists(aikarOversizedFile)) { + if (Files.exists(aikarOversizedFile)) {
+ try { + try {
+ CompoundTag aikarOversizedCompound = this.getOversizedData(chunkPos.x, chunkPos.z); + net.minecraft.nbt.CompoundTag aikarOversizedCompound = this.getOversizedData(chunkPos.x, chunkPos.z);
+ if (SerializableChunkData.getLastWorldSaveTime(compound) == SerializableChunkData.getLastWorldSaveTime(aikarOversizedCompound)) { + if (SerializableChunkData.getLastWorldSaveTime(compound) == SerializableChunkData.getLastWorldSaveTime(aikarOversizedCompound)) {
+ // best we got for an id. hope it's good enough + // best we got for an id. hope it's good enough
+ isAikarOversized = true; + isAikarOversized = true;
@ -236,14 +237,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ continue; + continue;
+ } + }
+ +
+ CompoundTag compound = null; + net.minecraft.nbt.CompoundTag compound = null;
+ +
+ // We do not know the compression type, as it's stored in the regionfile. So we need to try all of them + // We do not know the compression type, as it's stored in the regionfile. So we need to try all of them
+ RegionFileVersion compression = null; + RegionFileVersion compression = null;
+ for (RegionFileVersion compressionType : RegionFileVersion.VERSIONS.values()) { + for (RegionFileVersion compressionType : RegionFileVersion.VERSIONS.values()) {
+ try { + try {
+ DataInputStream in = new DataInputStream(compressionType.wrap(new ByteArrayInputStream(chunkData))); // typical java + DataInputStream in = new DataInputStream(compressionType.wrap(new ByteArrayInputStream(chunkData))); // typical java
+ compound = NbtIo.read((java.io.DataInput)in); + compound = net.minecraft.nbt.NbtIo.read((java.io.DataInput)in);
+ compression = compressionType; + compression = compressionType;
+ break; // reaches here iff readNBT does not throw + break; // reaches here iff readNBT does not throw
+ } catch (Exception ex) { + } catch (Exception ex) {
@ -397,63 +398,55 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ +
+ final boolean canRecalcHeader; // final forces compile fail on new constructor + final boolean canRecalcHeader; // final forces compile fail on new constructor
+ // Paper end - Attempt to recalculate regionfile header if it is corrupt + // Paper end - Attempt to recalculate regionfile header if it is corrupt
+
// Paper start - rewrite chunk system // Paper start - rewrite chunk system
@Override @Override
@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche public final ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.WriteData moonrise$startWrite(final net.minecraft.nbt.CompoundTag data, final ChunkPos pos) throws IOException {
throw new IllegalArgumentException("Expected directory, got " + String.valueOf(directory.toAbsolutePath())); @@ -74,6 +423,7 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
throw new IllegalArgumentException("Expected directory, got " + externalFileDir.toAbsolutePath());
} else { } else {
this.externalFileDir = directory; this.externalFileDir = externalFileDir;
+ this.canRecalcHeader = RegionFileStorage.isChunkDataFolder(this.externalFileDir); // Paper - add can recalc flag + this.canRecalcHeader = RegionFileStorage.isChunkDataFolder(this.externalFileDir); // Paper - add can recalc flag
this.offsets = this.header.asIntBuffer(); this.offsets = this.header.asIntBuffer();
((java.nio.Buffer) this.offsets).limit(1024); // CraftBukkit - decompile error this.offsets.limit(1024);
((java.nio.Buffer) this.header).position(4096); // CraftBukkit - decompile error this.header.position(4096);
@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche @@ -94,11 +444,13 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
RegionFile.LOGGER.warn("Region file {} has truncated header: {}", path, i);
}
- long j = Files.size(path); long size = Files.size(path);
+ final long j = Files.size(path); final long regionFileSize = j; // Paper - recalculate header on header corruption
- for (int k = 0; k < 1024; ++k) { - for (int i1 = 0; i1 < 1024; i1++) {
- int l = this.offsets.get(k);
+ boolean needsHeaderRecalc = false; // Paper - recalculate header on header corruption + boolean needsHeaderRecalc = false; // Paper - recalculate header on header corruption
+ boolean hasBackedUp = false; // Paper - recalculate header on header corruption + boolean hasBackedUp = false; // Paper - recalculate header on header corruption
+ for (int k = 0; k < 1024; ++k) { final int headerLocation = k; // Paper - we expect this to be the header location + for (int i1 = 0; i1 < 1024; i1++) { final int headerLocation = i1; // Paper - we expect this to be the header location
+ final int l = this.offsets.get(k); int i2 = this.offsets.get(i1);
if (i2 != 0) {
if (l != 0) { - int sectorNumber = getSectorNumber(i2);
- int i1 = RegionFile.getSectorNumber(l); - int numSectors = getNumSectors(i2);
- int j1 = RegionFile.getNumSectors(l); + final int sectorNumber = getSectorNumber(i2); // Paper - we expect this to be offset in file in sectors
+ final int i1 = RegionFile.getSectorNumber(l); final int offset = i1; // Paper - we expect this to be offset in file in sectors + int numSectors = getNumSectors(i2); // Paper - diff on change, we expect this to be sector length of region - watch out for reassignments
+ int j1 = RegionFile.getNumSectors(l); final int sectorLength; // Paper - diff on change, we expect this to be sector length of region - watch out for reassignments
// Spigot start // Spigot start
if (j1 == 255) { if (numSectors == 255) {
// We're maxed out, so we need to read the proper length from the section // We're maxed out, so we need to read the proper length from the section
@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche @@ -109,18 +461,62 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
j1 = (realLen.getInt(0) + 4) / 4096 + 1;
}
// Spigot end // Spigot end
+ sectorLength = j1; // Paper - diff on change, we expect this to be sector length of region if (sectorNumber < 2) {
LOGGER.warn("Region file {} has invalid sector at index: {}; sector {} overlaps with header", path, i1, sectorNumber);
if (i1 < 2) { - this.offsets.put(i1, 0);
RegionFile.LOGGER.warn("Region file {} has invalid sector at index: {}; sector {} overlaps with header", new Object[]{path, k, i1}); + //this.offsets.put(i1, 0); // Paper - we catch this, but need it in the header for the summary change
- this.offsets.put(k, 0); } else if (numSectors == 0) {
+ //this.offsets.put(k, 0); // Paper - we catch this, but need it in the header for the summary change LOGGER.warn("Region file {} has an invalid sector at index: {}; size has to be > 0", path, i1);
} else if (j1 == 0) { - this.offsets.put(i1, 0);
RegionFile.LOGGER.warn("Region file {} has an invalid sector at index: {}; size has to be > 0", path, k); + //this.offsets.put(i1, 0); // Paper - we catch this, but need it in the header for the summary change
- this.offsets.put(k, 0); } else if (sectorNumber * 4096L > size) {
+ //this.offsets.put(k, 0); // Paper - we catch this, but need it in the header for the summary change LOGGER.warn("Region file {} has an invalid sector at index: {}; sector {} is out of bounds", path, i1, sectorNumber);
} else if ((long) i1 * 4096L > j) { - this.offsets.put(i1, 0);
RegionFile.LOGGER.warn("Region file {} has an invalid sector at index: {}; sector {} is out of bounds", new Object[]{path, k, i1}); + //this.offsets.put(i1, 0); // Paper - we catch this, but need it in the header for the summary change
- this.offsets.put(k, 0);
+ //this.offsets.put(k, 0); // Paper - we catch this, but need it in the header for the summary change
} else { } else {
- this.usedSectors.force(i1, j1); - this.usedSectors.force(sectorNumber, numSectors);
+ //this.usedSectors.force(i1, j1); // Paper - move this down so we can check if it fails to allocate + //this.usedSectors.force(sectorNumber, numSectors); // Paper - move this down so we can check if it fails to allocate
+ } + }
+ // Paper start - recalculate header on header corruption + // Paper start - recalculate header on header corruption
+ if (offset < 2 || sectorLength <= 0 || ((long)offset * 4096L) > regionFileSize) { + if (sectorNumber < 2 || numSectors <= 0 || ((long)sectorNumber * 4096L) > size) {
+ if (canRecalcHeader) { + if (canRecalcHeader) {
+ LOGGER.error("Detected invalid header for regionfile " + this.path.toAbsolutePath() + "! Recalculating header..."); + LOGGER.error("Detected invalid header for regionfile " + this.path.toAbsolutePath() + "! Recalculating header...");
+ needsHeaderRecalc = true; + needsHeaderRecalc = true;
@ -471,7 +464,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ continue; + continue;
+ } + }
+ } + }
+ boolean failedToAllocate = !this.usedSectors.tryAllocate(offset, sectorLength); + boolean failedToAllocate = !this.usedSectors.tryAllocate(sectorNumber, numSectors);
+ if (failedToAllocate) { + if (failedToAllocate) {
+ LOGGER.error("Overlapping allocation by local chunk (" + (headerLocation & 31) + "," + (headerLocation >>> 5) + ") in regionfile " + this.path.toAbsolutePath()); + LOGGER.error("Overlapping allocation by local chunk (" + (headerLocation & 31) + "," + (headerLocation >>> 5) + ") in regionfile " + this.path.toAbsolutePath());
} }
@ -499,20 +492,19 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ } + }
+ // Paper end + // Paper end
} }
} }
@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche }
@@ -130,10 +526,35 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
} }
private Path getExternalChunkPath(ChunkPos chunkPos) { private Path getExternalChunkPath(ChunkPos chunkPos) {
- String s = "c." + chunkPos.x + "." + chunkPos.z + ".mcc"; - String string = "c." + chunkPos.x + "." + chunkPos.z + ".mcc";
+ String s = "c." + chunkPos.x + "." + chunkPos.z + ".mcc"; // Paper - diff on change + String string = "c." + chunkPos.x + "." + chunkPos.z + ".mcc"; // Paper - diff on change
return this.externalFileDir.resolve(string);
return this.externalFileDir.resolve(s);
} }
+ // Paper start + // Paper start
+ private static ChunkPos getOversizedChunkPair(Path file) { + private static @Nullable ChunkPos getOversizedChunkPair(Path file) {
+ String fileName = file.getFileName().toString(); + String fileName = file.getFileName().toString();
+ +
+ if (!fileName.startsWith("c.") || !fileName.endsWith(".mcc")) { + if (!fileName.startsWith("c.") || !fileName.endsWith(".mcc")) {
@ -537,81 +529,79 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ // Paper end + // Paper end
+ +
@Nullable @Nullable
public synchronized DataInputStream getChunkDataInputStream(ChunkPos pos) throws IOException { public synchronized DataInputStream getChunkDataInputStream(ChunkPos chunkPos) throws IOException {
int i = this.getOffset(pos); int offset = this.getOffset(chunkPos);
@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche @@ -155,30 +576,67 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
((java.nio.Buffer) bytebuffer).flip(); // CraftBukkit - decompile error byteBuffer.flip();
if (bytebuffer.remaining() < 5) { if (byteBuffer.remaining() < 5) {
RegionFile.LOGGER.error("Chunk {} header is truncated: expected {} but read {}", new Object[]{pos, l, bytebuffer.remaining()}); LOGGER.error("Chunk {} header is truncated: expected {} but read {}", chunkPos, i, byteBuffer.remaining());
+ // Paper start - recalculate header on regionfile corruption + // Paper start - recalculate header on regionfile corruption
+ if (this.canRecalcHeader && this.recalculateHeader()) { + if (this.canRecalcHeader && this.recalculateHeader()) {
+ return this.getChunkDataInputStream(pos); + return this.getChunkDataInputStream(chunkPos);
+ } + }
+ // Paper end - recalculate header on regionfile corruption + // Paper end - recalculate header on regionfile corruption
return null; return null;
} else { } else {
int i1 = bytebuffer.getInt(); int _int = byteBuffer.getInt();
@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche byte b = byteBuffer.get();
if (_int == 0) {
if (i1 == 0) { LOGGER.warn("Chunk {} is allocated, but stream is missing", chunkPos);
RegionFile.LOGGER.warn("Chunk {} is allocated, but stream is missing", pos);
+ // Paper start - recalculate header on regionfile corruption + // Paper start - recalculate header on regionfile corruption
+ if (this.canRecalcHeader && this.recalculateHeader()) { + if (this.canRecalcHeader && this.recalculateHeader()) {
+ return this.getChunkDataInputStream(pos); + return this.getChunkDataInputStream(chunkPos);
+ } + }
+ // Paper end - recalculate header on regionfile corruption + // Paper end - recalculate header on regionfile corruption
return null; return null;
} else { } else {
int j1 = i1 - 1; int i1 = _int - 1;
@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche if (isExternalStreamChunk(b)) {
if (RegionFile.isExternalStreamChunk(b0)) { if (i1 != 0) {
if (j1 != 0) { LOGGER.warn("Chunk has both internal and external streams");
RegionFile.LOGGER.warn("Chunk has both internal and external streams");
+ // Paper start - recalculate header on regionfile corruption + // Paper start - recalculate header on regionfile corruption
+ if (this.canRecalcHeader && this.recalculateHeader()) { + if (this.canRecalcHeader && this.recalculateHeader()) {
+ return this.getChunkDataInputStream(pos); + return this.getChunkDataInputStream(chunkPos);
+ } + }
+ // Paper end - recalculate header on regionfile corruption + // Paper end - recalculate header on regionfile corruption
} }
- return this.createExternalChunkInputStream(pos, RegionFile.getExternalChunkVersion(b0)); - return this.createExternalChunkInputStream(chunkPos, getExternalChunkVersion(b));
+ // Paper start - recalculate header on regionfile corruption + // Paper start - recalculate header on regionfile corruption
+ final DataInputStream ret = this.createExternalChunkInputStream(pos, RegionFile.getExternalChunkVersion(b0)); + final DataInputStream ret = this.createExternalChunkInputStream(chunkPos, getExternalChunkVersion(b));
+ if (ret == null && this.canRecalcHeader && this.recalculateHeader()) { + if (ret == null && this.canRecalcHeader && this.recalculateHeader()) {
+ return this.getChunkDataInputStream(pos); + return this.getChunkDataInputStream(chunkPos);
+ } + }
+ return ret; + return ret;
+ // Paper end - recalculate header on regionfile corruption + // Paper end - recalculate header on regionfile corruption
} else if (j1 > bytebuffer.remaining()) { } else if (i1 > byteBuffer.remaining()) {
RegionFile.LOGGER.error("Chunk {} stream is truncated: expected {} but read {}", new Object[]{pos, j1, bytebuffer.remaining()}); LOGGER.error("Chunk {} stream is truncated: expected {} but read {}", chunkPos, i1, byteBuffer.remaining());
+ // Paper start - recalculate header on regionfile corruption + // Paper start - recalculate header on regionfile corruption
+ if (this.canRecalcHeader && this.recalculateHeader()) { + if (this.canRecalcHeader && this.recalculateHeader()) {
+ return this.getChunkDataInputStream(pos); + return this.getChunkDataInputStream(chunkPos);
+ } + }
+ // Paper end - recalculate header on regionfile corruption + // Paper end - recalculate header on regionfile corruption
return null; return null;
} else if (j1 < 0) { } else if (i1 < 0) {
RegionFile.LOGGER.error("Declared size {} of chunk {} is negative", i1, pos); LOGGER.error("Declared size {} of chunk {} is negative", _int, chunkPos);
+ // Paper start - recalculate header on regionfile corruption + // Paper start - recalculate header on regionfile corruption
+ if (this.canRecalcHeader && this.recalculateHeader()) { + if (this.canRecalcHeader && this.recalculateHeader()) {
+ return this.getChunkDataInputStream(pos); + return this.getChunkDataInputStream(chunkPos);
+ } + }
+ // Paper end - recalculate header on regionfile corruption + // Paper end - recalculate header on regionfile corruption
return null; return null;
} else { } else {
JvmProfiler.INSTANCE.onRegionFileRead(this.info, pos, this.version, j1); JvmProfiler.INSTANCE.onRegionFileRead(this.info, chunkPos, this.version, i1);
- return this.createChunkInputStream(pos, b0, RegionFile.createStream(bytebuffer, j1)); - return this.createChunkInputStream(chunkPos, b, createStream(byteBuffer, i1));
+ // Paper start - recalculate header on regionfile corruption + // Paper start - recalculate header on regionfile corruption
+ final DataInputStream ret = this.createChunkInputStream(pos, b0, RegionFile.createStream(bytebuffer, j1)); + final DataInputStream ret = this.createChunkInputStream(chunkPos, b, createStream(byteBuffer, i1));
+ if (ret == null && this.canRecalcHeader && this.recalculateHeader()) { + if (ret == null && this.canRecalcHeader && this.recalculateHeader()) {
+ return this.getChunkDataInputStream(pos); + return this.getChunkDataInputStream(chunkPos);
+ } + }
+ return ret; + return ret;
+ // Paper end - recalculate header on regionfile corruption + // Paper end - recalculate header on regionfile corruption
} }
} }
} }
@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche @@ -361,9 +819,14 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
} }
private ByteBuffer createExternalStub() { private ByteBuffer createExternalStub() {
@ -620,22 +610,21 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ } + }
+ private ByteBuffer createExternalStub(RegionFileVersion version) { + private ByteBuffer createExternalStub(RegionFileVersion version) {
+ // Paper end - add version param + // Paper end - add version param
ByteBuffer bytebuffer = ByteBuffer.allocate(5); ByteBuffer byteBuffer = ByteBuffer.allocate(5);
byteBuffer.putInt(1);
- byteBuffer.put((byte)(this.version.getId() | 128));
+ byteBuffer.put((byte)(version.getId() | 128));
byteBuffer.flip();
return byteBuffer;
}
diff --git a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
index d263f78fa610ce6f6fb5a0f5e064e3d8335c2199..dad7f94b611cf0fc68b1a3878c458233f6bb6d61 100644
--- a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
+++ b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
@@ -23,6 +23,36 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
private final Path folder;
private final boolean sync;
bytebuffer.putInt(1);
- bytebuffer.put((byte) (this.version.getId() | 128));
+ bytebuffer.put((byte) (version.getId() | 128)); // Paper - replace with version param
((java.nio.Buffer) bytebuffer).flip(); // CraftBukkit - decompile error
return bytebuffer;
}
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
@@ -0,0 +0,0 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
}
}
// Paper end - rewrite chunk system
+ // Paper start - recalculate region file headers + // Paper start - recalculate region file headers
+ private final boolean isChunkData; + private final boolean isChunkData;
+ +
@ -666,40 +655,42 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ } + }
+ } + }
+ // Paper end + // Paper end
// Paper start - rewrite chunk system
protected RegionFileStorage(RegionStorageInfo storageKey, Path directory, boolean dsync) { // Paper - protected private static final int REGION_SHIFT = 5;
this.folder = directory; private static final int MAX_NON_EXISTING_CACHE = 1024 * 4;
this.sync = dsync; @@ -216,6 +246,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
this.info = storageKey; this.folder = folder;
this.sync = sync;
this.info = info;
+ this.isChunkData = isChunkDataFolder(this.folder); // Paper - recalculate region file headers + this.isChunkData = isChunkDataFolder(this.folder); // Paper - recalculate region file headers
} }
// Paper start - rewrite chunk system @org.jetbrains.annotations.Contract("_, false -> !null") @Nullable private RegionFile getRegionFile(ChunkPos chunkPos, boolean existingOnly) throws IOException { // CraftBukkit
@@ -0,0 +0,0 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise @@ -309,6 +340,19 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
try { }
if (datainputstream != null) {
nbttagcompound = NbtIo.read((DataInput) datainputstream);
+ // Paper start - recover from corrupt regionfile header
+ if (this.isChunkData) {
+ ChunkPos chunkPos = SerializableChunkData.getChunkCoordinate(nbttagcompound);
+ if (!chunkPos.equals(pos)) {
+ net.minecraft.server.MinecraftServer.LOGGER.error("Attempting to read chunk data at " + pos + " but got chunk data for " + chunkPos + " instead! Attempting regionfile recalculation for regionfile " + regionfile.getPath().toAbsolutePath());
+ if (regionfile.recalculateHeader()) {
+ return this.read(pos);
+ }
+ net.minecraft.server.MinecraftServer.LOGGER.error("Can't recalculate regionfile header, regenerating chunk " + pos + " for " + regionfile.getPath().toAbsolutePath());
+ return null;
+ }
+ }
+ // Paper end - recover from corrupt regionfile header
break label43;
}
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileVersion.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileVersion.java var4 = NbtIo.read(chunkDataInputStream);
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 + // Paper start - recover from corrupt regionfile header
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileVersion.java + if (this.isChunkData) {
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileVersion.java + ChunkPos headerChunkPos = SerializableChunkData.getChunkCoordinate(var4);
@@ -0,0 +0,0 @@ import org.slf4j.Logger; + if (!headerChunkPos.equals(chunkPos)) {
+ net.minecraft.server.MinecraftServer.LOGGER.error("Attempting to read chunk data at " + chunkPos + " but got chunk data for " + headerChunkPos + " instead! Attempting regionfile recalculation for regionfile " + regionFile.getPath().toAbsolutePath());
+ if (regionFile.recalculateHeader()) {
+ return this.read(chunkPos);
+ }
+ net.minecraft.server.MinecraftServer.LOGGER.error("Can't recalculate regionfile header, regenerating chunk " + chunkPos + " for " + regionFile.getPath().toAbsolutePath());
+ return null;
+ }
+ }
+ // Paper end - recover from corrupt regionfile header
}
return var4;
diff --git a/net/minecraft/world/level/chunk/storage/RegionFileVersion.java b/net/minecraft/world/level/chunk/storage/RegionFileVersion.java
index 0c739ca5b01ac0ec35a11fd01c5fc65de97c2852..de7deee4b79c969a7797bd57b657a16404c15303 100644
--- a/net/minecraft/world/level/chunk/storage/RegionFileVersion.java
+++ b/net/minecraft/world/level/chunk/storage/RegionFileVersion.java
@@ -21,7 +21,7 @@ import org.slf4j.Logger;
public class RegionFileVersion { public class RegionFileVersion {
private static final Logger LOGGER = LogUtils.getLogger(); private static final Logger LOGGER = LogUtils.getLogger();
@ -708,11 +699,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
private static final Object2ObjectMap<String, RegionFileVersion> VERSIONS_BY_NAME = new Object2ObjectOpenHashMap<>(); private static final Object2ObjectMap<String, RegionFileVersion> VERSIONS_BY_NAME = new Object2ObjectOpenHashMap<>();
public static final RegionFileVersion VERSION_GZIP = register( public static final RegionFileVersion VERSION_GZIP = register(
new RegionFileVersion( new RegionFileVersion(
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/SerializableChunkData.java b/src/main/java/net/minecraft/world/level/chunk/storage/SerializableChunkData.java diff --git a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 index 70a9972252576e039ac126f6057a6ed66b80cdfc..d783c3580ea274a0a9cb07449eb8037bc5a04d76 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/SerializableChunkData.java --- a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/SerializableChunkData.java +++ b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
@@ -0,0 +0,0 @@ public record SerializableChunkData(Registry<Biome> biomeRegistry, ChunkPos chun @@ -120,6 +120,18 @@ public record SerializableChunkData(
} }
} }
// Paper end - guard against serializing mismatching coordinates // Paper end - guard against serializing mismatching coordinates
@ -731,12 +722,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
// Paper start - Do not let the server load chunks from newer versions // 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 int CURRENT_DATA_VERSION = net.minecraft.SharedConstants.getCurrentVersion().getDataVersion().getVersion();
@@ -0,0 +0,0 @@ public record SerializableChunkData(Registry<Biome> biomeRegistry, ChunkPos chun @@ -604,7 +616,7 @@ public record SerializableChunkData(
nbttagcompound.putInt("xPos", this.chunkPos.x); compoundTag.putInt("xPos", this.chunkPos.x);
nbttagcompound.putInt("yPos", this.minSectionY); compoundTag.putInt("yPos", this.minSectionY);
nbttagcompound.putInt("zPos", this.chunkPos.z); compoundTag.putInt("zPos", this.chunkPos.z);
- nbttagcompound.putLong("LastUpdate", this.lastUpdateTime); - compoundTag.putLong("LastUpdate", this.lastUpdateTime);
+ nbttagcompound.putLong("LastUpdate", this.lastUpdateTime); // Paper - Diff on change + compoundTag.putLong("LastUpdate", this.lastUpdateTime); // Paper - Diff on change
nbttagcompound.putLong("InhabitedTime", this.inhabitedTime); compoundTag.putLong("InhabitedTime", this.inhabitedTime);
nbttagcompound.putString("Status", BuiltInRegistries.CHUNK_STATUS.getKey(this.chunkStatus).toString()); compoundTag.putString("Status", BuiltInRegistries.CHUNK_STATUS.getKey(this.chunkStatus).toString());
DataResult<Tag> dataresult; // CraftBukkit - decompile error if (this.blendingData != null) {

View file

@ -30,12 +30,13 @@ This patch also specifically optimises other areas of code to
use PoiAccess. For example, some villager AI and portaling code use PoiAccess. For example, some villager AI and portaling code
had to be specifically modified. had to be specifically modified.
diff --git a/src/main/java/io/papermc/paper/util/PoiAccess.java b/src/main/java/io/papermc/paper/util/PoiAccess.java
diff --git a/io/papermc/paper/util/PoiAccess.java b/io/papermc/paper/util/PoiAccess.java
new file mode 100644 new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 index 0000000000000000000000000000000000000000..f39294b1f83c4022be5ced4da781103a1eee2daf
--- /dev/null --- /dev/null
+++ b/src/main/java/io/papermc/paper/util/PoiAccess.java +++ b/io/papermc/paper/util/PoiAccess.java
@@ -0,0 +0,0 @@ @@ -0,0 +1,806 @@
+package io.papermc.paper.util; +package io.papermc.paper.util;
+ +
+import ca.spottedleaf.moonrise.common.util.CoordinateUtils; +import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
@ -842,38 +843,38 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ throw new RuntimeException(); + throw new RuntimeException();
+ } + }
+} +}
diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/AcquirePoi.java b/src/main/java/net/minecraft/world/entity/ai/behavior/AcquirePoi.java diff --git a/net/minecraft/world/entity/ai/behavior/AcquirePoi.java b/net/minecraft/world/entity/ai/behavior/AcquirePoi.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 index 9de13a78b2a8be181c02ab330bfa9abb936a83db..b9174ae7e3a3e2de2d570b95ab5012ac3c3a2eda 100644
--- a/src/main/java/net/minecraft/world/entity/ai/behavior/AcquirePoi.java --- a/net/minecraft/world/entity/ai/behavior/AcquirePoi.java
+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/AcquirePoi.java +++ b/net/minecraft/world/entity/ai/behavior/AcquirePoi.java
@@ -0,0 +0,0 @@ public class AcquirePoi { @@ -84,12 +84,16 @@ public class AcquirePoi {
return true; return true;
} }
}; };
- Set<Pair<Holder<PoiType>, BlockPos>> set = poiManager.findAllClosestFirstWithType( - Set<Pair<Holder<PoiType>, BlockPos>> set = poiManager.findAllClosestFirstWithType(
- poiPredicate, predicate2, entity.blockPosition(), 48, PoiManager.Occupancy.HAS_SPACE - acquirablePois, predicate1, mob.blockPosition(), 48, PoiManager.Occupancy.HAS_SPACE
- ) - )
- .limit(5L) - .limit(5L)
- .filter(pairx -> worldPosBiPredicate.test(world, (BlockPos)pairx.getSecond())) - .filter(pair1 -> predicate.test(level, pair1.getSecond()))
- .collect(Collectors.toSet()); - .collect(Collectors.toSet());
+ // Paper start - optimise POI access + // Paper start - optimise POI access
+ final java.util.List<Pair<Holder<PoiType>, BlockPos>> poiposes = new java.util.ArrayList<>(); + final java.util.List<Pair<Holder<PoiType>, BlockPos>> poiposes = new java.util.ArrayList<>();
+ io.papermc.paper.util.PoiAccess.findNearestPoiPositions(poiManager, poiPredicate, predicate2, entity.blockPosition(), 48, 48*48, PoiManager.Occupancy.HAS_SPACE, false, 5, poiposes); + io.papermc.paper.util.PoiAccess.findNearestPoiPositions(poiManager, acquirablePois, predicate1, mob.blockPosition(), 48, 48*48, PoiManager.Occupancy.HAS_SPACE, false, 5, poiposes);
+ final Set<Pair<Holder<PoiType>, BlockPos>> set = new java.util.HashSet<>(poiposes.size()); + final Set<Pair<Holder<PoiType>, BlockPos>> set = new java.util.HashSet<>(poiposes.size());
+ for (final Pair<Holder<PoiType>, BlockPos> poiPose : poiposes) { + for (final Pair<Holder<PoiType>, BlockPos> poiPose : poiposes) {
+ if (worldPosBiPredicate.test(world, poiPose.getSecond())) { + if (predicate.test(level, poiPose.getSecond())) {
+ set.add(poiPose); + set.add(poiPose);
+ } + }
+ } + }
+ // Paper end - optimise POI access + // Paper end - optimise POI access
Path path = findPathToPois(entity, set); Path path = findPathToPois(mob, set);
if (path != null && path.canReach()) { if (path != null && path.canReach()) {
BlockPos blockPos = path.getTarget(); BlockPos target = path.getTarget();
diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java diff --git a/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java b/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 index 6e9325f0800a35637fdec5edb8a514ea03741762..066faa704338c573472381e1ebd063e0d52aaaa4 100644
--- a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java --- a/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java
+++ b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java +++ b/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java
@@ -0,0 +0,0 @@ public class NearestBedSensor extends Sensor<Mob> { @@ -53,11 +53,12 @@ public class NearestBedSensor extends Sensor<Mob> {
return true; return true;
} }
}; };
@ -889,82 +890,82 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ Path path = AcquirePoi.findPathToPois(entity, new java.util.HashSet<>(poiposes)); + Path path = AcquirePoi.findPathToPois(entity, new java.util.HashSet<>(poiposes));
+ // Paper end - optimise POI access + // Paper end - optimise POI access
if (path != null && path.canReach()) { if (path != null && path.canReach()) {
BlockPos blockPos = path.getTarget(); BlockPos target = path.getTarget();
Optional<Holder<PoiType>> optional = poiManager.getType(blockPos); Optional<Holder<PoiType>> type = poiManager.getType(target);
diff --git a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java diff --git a/net/minecraft/world/entity/ai/village/poi/PoiManager.java b/net/minecraft/world/entity/ai/village/poi/PoiManager.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 index 5c5724f5e3ad640f55aecbc1d8f71d1f59ecdc62..618fc0eb4fe70e46e55f3aa28e8eac1d2d01b6d9 100644
--- a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java --- a/net/minecraft/world/entity/ai/village/poi/PoiManager.java
+++ b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java +++ b/net/minecraft/world/entity/ai/village/poi/PoiManager.java
@@ -0,0 +0,0 @@ public class PoiManager extends SectionStorage<PoiSection, PoiSection.Packed> im @@ -254,36 +254,47 @@ public class PoiManager extends SectionStorage<PoiSection, PoiSection.Packed> im
public Optional<BlockPos> find( public Optional<BlockPos> find(
Predicate<Holder<PoiType>> typePredicate, Predicate<BlockPos> posPredicate, BlockPos pos, int radius, PoiManager.Occupancy occupationStatus Predicate<Holder<PoiType>> typePredicate, Predicate<BlockPos> posPredicate, BlockPos pos, int distance, PoiManager.Occupancy status
) { ) {
- return this.findAll(typePredicate, posPredicate, pos, radius, occupationStatus).findFirst(); - return this.findAll(typePredicate, posPredicate, pos, distance, status).findFirst();
+ // Paper start - re-route to faster logic + // Paper start - re-route to faster logic
+ BlockPos ret = io.papermc.paper.util.PoiAccess.findAnyPoiPosition(this, typePredicate, posPredicate, pos, radius, occupationStatus, false); + BlockPos ret = io.papermc.paper.util.PoiAccess.findAnyPoiPosition(this, typePredicate, posPredicate, pos, distance, status, false);
+ return Optional.ofNullable(ret); + return Optional.ofNullable(ret);
+ // Paper end + // Paper end
} }
public Optional<BlockPos> findClosest(Predicate<Holder<PoiType>> typePredicate, BlockPos pos, int radius, PoiManager.Occupancy occupationStatus) { public Optional<BlockPos> findClosest(Predicate<Holder<PoiType>> typePredicate, BlockPos pos, int distance, PoiManager.Occupancy status) {
- return this.getInRange(typePredicate, pos, radius, occupationStatus) - return this.getInRange(typePredicate, pos, distance, status).map(PoiRecord::getPos).min(Comparator.comparingDouble(blockPos -> blockPos.distSqr(pos)));
- .map(PoiRecord::getPos)
- .min(Comparator.comparingDouble(poiPos -> poiPos.distSqr(pos)));
+ // Paper start - re-route to faster logic + // Paper start - re-route to faster logic
+ BlockPos closestPos = io.papermc.paper.util.PoiAccess.findClosestPoiDataPosition(this, typePredicate, null, pos, radius, radius * radius, occupationStatus, false); + BlockPos closestPos = io.papermc.paper.util.PoiAccess.findClosestPoiDataPosition(this, typePredicate, null, pos, distance, distance * distance, status, false);
+ return Optional.ofNullable(closestPos); + return Optional.ofNullable(closestPos);
+ // Paper end - re-route to faster logic + // Paper end - re-route to faster logic
} }
public Optional<Pair<Holder<PoiType>, BlockPos>> findClosestWithType( public Optional<Pair<Holder<PoiType>, BlockPos>> findClosestWithType(
Predicate<Holder<PoiType>> typePredicate, BlockPos pos, int radius, PoiManager.Occupancy occupationStatus Predicate<Holder<PoiType>> typePredicate, BlockPos pos, int distance, PoiManager.Occupancy status
) { ) {
- return this.getInRange(typePredicate, pos, radius, occupationStatus) - return this.getInRange(typePredicate, pos, distance, status)
- .min(Comparator.comparingDouble(poi -> poi.getPos().distSqr(pos))) - .min(Comparator.comparingDouble(poiRecord -> poiRecord.getPos().distSqr(pos)))
- .map(poi -> Pair.of(poi.getPoiType(), poi.getPos())); - .map(poiRecord -> Pair.of(poiRecord.getPoiType(), poiRecord.getPos()));
+ // Paper start - re-route to faster logic + // Paper start - re-route to faster logic
+ return Optional.ofNullable(io.papermc.paper.util.PoiAccess.findClosestPoiDataTypeAndPosition( + return Optional.ofNullable(io.papermc.paper.util.PoiAccess.findClosestPoiDataTypeAndPosition(
+ this, typePredicate, null, pos, radius, radius * radius, occupationStatus, false + this, typePredicate, null, pos, distance, distance * distance, status, false
+ )); + ));
+ // Paper end - re-route to faster logic + // Paper end - re-route to faster logic
} }
public Optional<BlockPos> findClosest( public Optional<BlockPos> findClosest(
Predicate<Holder<PoiType>> typePredicate, Predicate<BlockPos> posPredicate, BlockPos pos, int radius, PoiManager.Occupancy occupationStatus Predicate<Holder<PoiType>> typePredicate, Predicate<BlockPos> posPredicate, BlockPos pos, int distance, PoiManager.Occupancy status
) { ) {
- return this.getInRange(typePredicate, pos, radius, occupationStatus) - return this.getInRange(typePredicate, pos, distance, status)
- .map(PoiRecord::getPos) - .map(PoiRecord::getPos)
- .filter(posPredicate) - .filter(posPredicate)
- .min(Comparator.comparingDouble(poiPos -> poiPos.distSqr(pos))); - .min(Comparator.comparingDouble(blockPos -> blockPos.distSqr(pos)));
+ // Paper start - re-route to faster logic + // Paper start - re-route to faster logic
+ BlockPos closestPos = io.papermc.paper.util.PoiAccess.findClosestPoiDataPosition(this, typePredicate, posPredicate, pos, radius, radius * radius, occupationStatus, false); + BlockPos closestPos = io.papermc.paper.util.PoiAccess.findClosestPoiDataPosition(this, typePredicate, posPredicate, pos, distance, distance * distance, status, false);
+ return Optional.ofNullable(closestPos); + return Optional.ofNullable(closestPos);
+ // Paper end - re-route to faster logic + // Paper end - re-route to faster logic
} }
public Optional<BlockPos> take(Predicate<Holder<PoiType>> typePredicate, BiPredicate<Holder<PoiType>, BlockPos> posPredicate, BlockPos pos, int radius) { public Optional<BlockPos> take(
- return this.getInRange(typePredicate, pos, radius, PoiManager.Occupancy.HAS_SPACE) Predicate<Holder<PoiType>> typePredicate, BiPredicate<Holder<PoiType>, BlockPos> combinedTypePosPredicate, BlockPos pos, int distance
- .filter(poi -> posPredicate.test(poi.getPoiType(), poi.getPos())) ) {
- return this.getInRange(typePredicate, pos, distance, PoiManager.Occupancy.HAS_SPACE)
- .filter(poiRecord -> combinedTypePosPredicate.test(poiRecord.getPoiType(), poiRecord.getPos()))
- .findFirst() - .findFirst()
+ // Paper start - re-route to faster logic + // Paper start - re-route to faster logic
+ final @javax.annotation.Nullable PoiRecord closest = io.papermc.paper.util.PoiAccess.findClosestPoiDataRecord( + final @javax.annotation.Nullable PoiRecord closest = io.papermc.paper.util.PoiAccess.findClosestPoiDataRecord(
+ this, typePredicate, posPredicate, pos, radius, radius * radius, Occupancy.HAS_SPACE, false + this, typePredicate, combinedTypePosPredicate, pos, distance, distance * distance, Occupancy.HAS_SPACE, false
+ ); + );
+ return Optional.ofNullable(closest) + return Optional.ofNullable(closest)
+ // Paper end - re-route to faster logic + // Paper end - re-route to faster logic
.map(poi -> { .map(poiRecord -> {
poi.acquireTicket(); poiRecord.acquireTicket();
return poi.getPos(); return poiRecord.getPos();
@@ -0,0 +0,0 @@ public class PoiManager extends SectionStorage<PoiSection, PoiSection.Packed> im @@ -298,8 +309,21 @@ public class PoiManager extends SectionStorage<PoiSection, PoiSection.Packed> im
int radius, int distance,
RandomSource random RandomSource random
) { ) {
- List<PoiRecord> list = Util.toShuffledList(this.getInRange(typePredicate, pos, radius, occupationStatus), random); - List<PoiRecord> list = Util.toShuffledList(this.getInRange(typePredicate, pos, distance, status), random);
- return list.stream().filter(poi -> positionPredicate.test(poi.getPos())).findFirst().map(PoiRecord::getPos); - return list.stream().filter(poiRecord -> posPredicate.test(poiRecord.getPos())).findFirst().map(PoiRecord::getPos);
+ // Paper start - re-route to faster logic + // Paper start - re-route to faster logic
+ List<PoiRecord> list = new java.util.ArrayList<>(); + List<PoiRecord> list = new java.util.ArrayList<>();
+ io.papermc.paper.util.PoiAccess.findAnyPoiRecords( + io.papermc.paper.util.PoiAccess.findAnyPoiRecords(
+ this, typePredicate, positionPredicate, pos, radius, occupationStatus, false, Integer.MAX_VALUE, list + this, typePredicate, posPredicate, pos, distance, status, false, Integer.MAX_VALUE, list
+ ); + );
+ +
+ // the old method shuffled the list and then tried to find the first element in it that + // the old method shuffled the list and then tried to find the first element in it that
@ -979,11 +980,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
} }
public boolean release(BlockPos pos) { public boolean release(BlockPos pos) {
diff --git a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java diff --git a/net/minecraft/world/entity/ai/village/poi/PoiSection.java b/net/minecraft/world/entity/ai/village/poi/PoiSection.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 index 39cd1e3d8192d7077d6b7864d33933097cc6b986..b92ba4d194fd3af94c7af5d8e150fc4297c73ab8 100644
--- a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java --- a/net/minecraft/world/entity/ai/village/poi/PoiSection.java
+++ b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java +++ b/net/minecraft/world/entity/ai/village/poi/PoiSection.java
@@ -0,0 +0,0 @@ import org.slf4j.Logger; @@ -26,7 +26,7 @@ import org.slf4j.Logger;
public class PoiSection implements ca.spottedleaf.moonrise.patches.chunk_system.level.poi.ChunkSystemPoiSection { // Paper - rewrite chunk system public class PoiSection implements ca.spottedleaf.moonrise.patches.chunk_system.level.poi.ChunkSystemPoiSection { // Paper - rewrite chunk system
private static final Logger LOGGER = LogUtils.getLogger(); private static final Logger LOGGER = LogUtils.getLogger();
private final Short2ObjectMap<PoiRecord> records = new Short2ObjectOpenHashMap<>(); private final Short2ObjectMap<PoiRecord> records = new Short2ObjectOpenHashMap<>();
@ -992,48 +993,43 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
private final Runnable setDirty; private final Runnable setDirty;
private boolean isValid; private boolean isValid;
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java diff --git a/net/minecraft/world/level/chunk/storage/SectionStorage.java b/net/minecraft/world/level/chunk/storage/SectionStorage.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 index 778bd73a938c94ecb85ca0f8b686ff4e1baee040..79d4ce7712f16995b0de3be86477fb43ab3961d7 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java --- a/net/minecraft/world/level/chunk/storage/SectionStorage.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java +++ b/net/minecraft/world/level/chunk/storage/SectionStorage.java
@@ -0,0 +0,0 @@ public class SectionStorage<R, P> implements AutoCloseable, ca.spottedleaf.moonr @@ -131,11 +131,11 @@ public class SectionStorage<R, P> implements AutoCloseable, ca.spottedleaf.moonr
} }
@Nullable @Nullable
- protected Optional<R> get(long pos) { - protected Optional<R> get(long sectionKey) {
+ public Optional<R> get(long pos) { // Paper - public + public Optional<R> get(long sectionKey) { // Paper - public
return this.storage.get(pos); return this.storage.get(sectionKey);
} }
- protected Optional<R> getOrLoad(long pos) { - protected Optional<R> getOrLoad(long sectionKey) {
+ public Optional<R> getOrLoad(long pos) { // Paper - public + public Optional<R> getOrLoad(long sectionKey) { // Paper - public
if (this.outsideStoredRange(pos)) { if (this.outsideStoredRange(sectionKey)) {
return Optional.empty(); return Optional.empty();
} else { } else {
diff --git a/src/main/java/net/minecraft/world/level/portal/PortalForcer.java b/src/main/java/net/minecraft/world/level/portal/PortalForcer.java diff --git a/net/minecraft/world/level/portal/PortalForcer.java b/net/minecraft/world/level/portal/PortalForcer.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 index ada2da62d3a40d67e64f5f8d7299f78b5c6f53cb..90ae71eb8cc7f925eb212f39731d70f3bff5ef0a 100644
--- a/src/main/java/net/minecraft/world/level/portal/PortalForcer.java --- a/net/minecraft/world/level/portal/PortalForcer.java
+++ b/src/main/java/net/minecraft/world/level/portal/PortalForcer.java +++ b/net/minecraft/world/level/portal/PortalForcer.java
@@ -0,0 +0,0 @@ public class PortalForcer { @@ -48,13 +48,38 @@ public class PortalForcer {
// int i = flag ? 16 : 128; PoiManager poiManager = this.level.getPoiManager();
// int i = isNether ? 16 : 128;
// CraftBukkit end // CraftBukkit end
- poiManager.ensureLoadedAndValid(this.level, exitPos, i);
- villageplace.ensureLoadedAndValid(this.level, blockposition, i); - return poiManager.getInSquare(holder -> holder.is(PoiTypes.NETHER_PORTAL), exitPos, i, PoiManager.Occupancy.ANY)
- Stream<BlockPos> stream = villageplace.getInSquare((holder) -> { // CraftBukkit - decompile error - .map(PoiRecord::getPos)
- return holder.is(PoiTypes.NETHER_PORTAL); - .filter(worldBorder::isWithinBounds)
- }, blockposition, i, PoiManager.Occupancy.ANY).map(PoiRecord::getPos); - .filter(pos -> !(this.level.getTypeKey() == net.minecraft.world.level.dimension.LevelStem.NETHER && this.level.paperConfig().environment.netherCeilingVoidDamageHeight.test(v -> pos.getY() >= v))) // Paper - Configurable nether ceiling damage
- - .filter(blockPos -> this.level.getBlockState(blockPos).hasProperty(BlockStateProperties.HORIZONTAL_AXIS))
- Objects.requireNonNull(worldborder); - .min(Comparator.<BlockPos>comparingDouble(blockPos -> blockPos.distSqr(exitPos)).thenComparingInt(Vec3i::getY));
- return stream.filter(worldborder::isWithinBounds).filter(pos -> !(this.level.getTypeKey() == net.minecraft.world.level.dimension.LevelStem.NETHER && this.level.paperConfig().environment.netherCeilingVoidDamageHeight.test(v -> pos.getY() >= v))).filter((blockposition1) -> { // Paper - Configurable nether ceiling damage
- return this.level.getBlockState(blockposition1).hasProperty(BlockStateProperties.HORIZONTAL_AXIS);
- }).min(Comparator.comparingDouble((BlockPos blockposition1) -> { // CraftBukkit - decompile error
- return blockposition1.distSqr(blockposition);
- }).thenComparingInt(Vec3i::getY));
+ // Paper start - optimise portals + // Paper start - optimise portals
+ Optional<PoiRecord> optional;
+ java.util.List<PoiRecord> records = new java.util.ArrayList<>(); + java.util.List<PoiRecord> records = new java.util.ArrayList<>();
+ io.papermc.paper.util.PoiAccess.findClosestPoiDataRecords( + io.papermc.paper.util.PoiAccess.findClosestPoiDataRecords(
+ villageplace, + poiManager,
+ type -> type.is(PoiTypes.NETHER_PORTAL), + type -> type.is(PoiTypes.NETHER_PORTAL),
+ (BlockPos pos) -> { + (BlockPos pos) -> {
+ net.minecraft.world.level.chunk.ChunkAccess lowest = this.level.getChunk(pos.getX() >> 4, pos.getZ() >> 4, net.minecraft.world.level.chunk.status.ChunkStatus.EMPTY); + net.minecraft.world.level.chunk.ChunkAccess lowest = this.level.getChunk(pos.getX() >> 4, pos.getZ() >> 4, net.minecraft.world.level.chunk.status.ChunkStatus.EMPTY);
@ -1042,12 +1038,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ // why would we generate the chunk? + // why would we generate the chunk?
+ return false; + return false;
+ } + }
+ if (!worldborder.isWithinBounds(pos) || (this.level.getTypeKey() == net.minecraft.world.level.dimension.LevelStem.NETHER && this.level.paperConfig().environment.netherCeilingVoidDamageHeight.test(v -> pos.getY() >= v))) { // Paper - Configurable nether ceiling damage + if (!worldBorder.isWithinBounds(pos) || (this.level.getTypeKey() == net.minecraft.world.level.dimension.LevelStem.NETHER && this.level.paperConfig().environment.netherCeilingVoidDamageHeight.test(v -> pos.getY() >= v))) { // Paper - Configurable nether ceiling damage
+ return false; + return false;
+ } + }
+ return lowest.getBlockState(pos).hasProperty(BlockStateProperties.HORIZONTAL_AXIS); + return lowest.getBlockState(pos).hasProperty(BlockStateProperties.HORIZONTAL_AXIS);
+ }, + },
+ blockposition, i, Double.MAX_VALUE, PoiManager.Occupancy.ANY, true, records + exitPos, i, Double.MAX_VALUE, PoiManager.Occupancy.ANY, true, records
+ ); + );
+ +
+ // this gets us most of the way there, but we bias towards lower y values. + // this gets us most of the way there, but we bias towards lower y values.