Files
KaiijuMC/patches/server/0004-Kaiiju-RegionFormat-Linear.patch
2023-04-01 04:22:05 +03:00

1024 lines
56 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: "Sofiane H. Djerbi" <46628754+kugge@users.noreply.github.com>
Date: Fri, 10 Feb 2023 22:21:56 +0200
Subject: [PATCH] Kaiiju RegionFormat Linear
Documentation: https://github.com/xymb-endcrystalme/LinearRegionFileFormatTools
Copyright xymb@endcrystal.me
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/build.gradle.kts b/build.gradle.kts
index 6dd416f65755ca0542e6661b8716672264120d59..4686019a152114e63e997ee103fc8424b24b4581 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -13,6 +13,10 @@ dependencies {
exclude("io.papermc.paper", "paper-api")
}
// Folia end
+ // Kaiiju start - Linear format
+ implementation("com.github.luben:zstd-jni:1.5.4-1")
+ implementation("org.lz4:lz4-java:1.8.0")
+ // Kaiiju end
// Paper start
implementation("org.jline:jline-terminal-jansi:3.21.0")
implementation("net.minecrell:terminalconsoleappender:1.3.0")
diff --git a/src/main/java/com/destroystokyo/paper/io/PaperFileIOThread.java b/src/main/java/com/destroystokyo/paper/io/PaperFileIOThread.java
index f2c27e0ac65be4b75c1d86ef6fd45fdb538d96ac..00724993d0448454d14a47652b039b88052b8a4f 100644
--- a/src/main/java/com/destroystokyo/paper/io/PaperFileIOThread.java
+++ b/src/main/java/com/destroystokyo/paper/io/PaperFileIOThread.java
@@ -314,8 +314,8 @@ public final class PaperFileIOThread extends QueueExecutorThread {
public abstract void writeData(final int x, final int z, final CompoundTag compound) throws IOException;
public abstract CompoundTag readData(final int x, final int z) throws IOException;
- public abstract <T> T computeForRegionFile(final int chunkX, final int chunkZ, final Function<RegionFile, T> function);
- public abstract <T> T computeForRegionFileIfLoaded(final int chunkX, final int chunkZ, final Function<RegionFile, T> function);
+ public abstract <T> T computeForRegionFile(final int chunkX, final int chunkZ, final Function<dev.kaiijumc.kaiiju.region.AbstractRegionFile, T> function); // Kaiiju
+ public abstract <T> T computeForRegionFileIfLoaded(final int chunkX, final int chunkZ, final Function<dev.kaiijumc.kaiiju.region.AbstractRegionFile, T> function); // Kaiiju
public static final class InProgressWrite {
public long writeCounter;
diff --git a/src/main/java/dev/kaiijumc/kaiiju/region/AbstractRegionFile.java b/src/main/java/dev/kaiijumc/kaiiju/region/AbstractRegionFile.java
new file mode 100644
index 0000000000000000000000000000000000000000..249303116d3cfadd078ebf0ae6e44bf99eed6a47
--- /dev/null
+++ b/src/main/java/dev/kaiijumc/kaiiju/region/AbstractRegionFile.java
@@ -0,0 +1,31 @@
+package dev.kaiijumc.kaiiju.region;
+
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.world.level.ChunkPos;
+import net.minecraft.world.level.chunk.ChunkStatus;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.concurrent.locks.ReentrantLock;
+
+public interface AbstractRegionFile {
+ void flush() throws IOException;
+ void clear(ChunkPos pos) throws IOException;
+ void close() throws IOException;
+ void setStatus(int x, int z, ChunkStatus status);
+ void setOversized(int x, int z, boolean b) throws IOException;
+
+ boolean hasChunk(ChunkPos pos);
+ boolean doesChunkExist(ChunkPos pos) throws Exception;
+ boolean isOversized(int x, int z);
+ boolean recalculateHeader() throws IOException;
+
+ DataOutputStream getChunkDataOutputStream(ChunkPos pos) throws IOException;
+ DataInputStream getChunkDataInputStream(ChunkPos pos) throws IOException;
+ CompoundTag getOversizedData(int x, int z) throws IOException;
+ ChunkStatus getStatusIfCached(int x, int z);
+ ReentrantLock getFileLock();
+ Path getRegionFile();
+}
diff --git a/src/main/java/dev/kaiijumc/kaiiju/region/AbstractRegionFileFactory.java b/src/main/java/dev/kaiijumc/kaiiju/region/AbstractRegionFileFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..dcfbabf54b19a4c29d5c95830242c5c26bf2f4aa
--- /dev/null
+++ b/src/main/java/dev/kaiijumc/kaiiju/region/AbstractRegionFileFactory.java
@@ -0,0 +1,29 @@
+package dev.kaiijumc.kaiiju.region;
+
+import net.minecraft.world.level.chunk.storage.RegionFile;
+import net.minecraft.world.level.chunk.storage.RegionFileVersion;
+
+import java.io.IOException;
+import java.nio.file.Path;
+
+public class AbstractRegionFileFactory {
+ public static AbstractRegionFile getAbstractRegionFile(int linearCompression, Path file, Path directory, boolean dsync) throws IOException {
+ return getAbstractRegionFile(linearCompression, file, directory, RegionFileVersion.VERSION_DEFLATE, dsync);
+ }
+
+ public static AbstractRegionFile getAbstractRegionFile(int linearCompression, Path file, Path directory, boolean dsync, boolean canRecalcHeader) throws IOException {
+ return getAbstractRegionFile(linearCompression, file, directory, RegionFileVersion.VERSION_DEFLATE, dsync, canRecalcHeader);
+ }
+
+ public static AbstractRegionFile getAbstractRegionFile(int linearCompression, Path file, Path directory, RegionFileVersion outputChunkStreamVersion, boolean dsync) throws IOException {
+ return getAbstractRegionFile(linearCompression, file, directory, outputChunkStreamVersion, dsync, false);
+ }
+
+ public static AbstractRegionFile getAbstractRegionFile(int linearCompression, Path file, Path directory, RegionFileVersion outputChunkStreamVersion, boolean dsync, boolean canRecalcHeader) throws IOException {
+ if (file.toString().endsWith(".linear")) {
+ return new LinearRegionFile(file, linearCompression);
+ } else {
+ return new RegionFile(file, directory, outputChunkStreamVersion, dsync, canRecalcHeader);
+ }
+ }
+}
diff --git a/src/main/java/dev/kaiijumc/kaiiju/region/LinearRegionFile.java b/src/main/java/dev/kaiijumc/kaiiju/region/LinearRegionFile.java
new file mode 100644
index 0000000000000000000000000000000000000000..f05d6313012560c32a3ec574b7169ec8e8930282
--- /dev/null
+++ b/src/main/java/dev/kaiijumc/kaiiju/region/LinearRegionFile.java
@@ -0,0 +1,334 @@
+package dev.kaiijumc.kaiiju.region;
+
+import com.github.luben.zstd.ZstdInputStream;
+import com.github.luben.zstd.ZstdOutputStream;
+import com.mojang.logging.LogUtils;
+import net.jpountz.lz4.LZ4Compressor;
+import net.jpountz.lz4.LZ4Factory;
+import net.jpountz.lz4.LZ4FastDecompressor;
+import net.jpountz.xxhash.XXHashFactory;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.world.level.ChunkPos;
+import net.minecraft.world.level.chunk.ChunkStatus;
+import org.slf4j.Logger;
+
+import javax.annotation.Nullable;
+import java.io.*;
+import java.nio.ByteBuffer;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.util.ArrayList;
+import java.util.concurrent.locks.ReentrantLock;
+
+public class LinearRegionFile extends Thread implements AbstractRegionFile {
+ private static final Logger LOGGER = LogUtils.getLogger();
+ private final byte[][] buffer = new byte[32*32][];
+ private final int[] bufferUncompressedSize = new int[32*32];
+
+ private final Object markedToSaveLock = new Object();
+ private final ChunkStatus[] statuses = new ChunkStatus[32 * 32];
+
+ private boolean markedToSave = false;
+ private boolean close = false;
+
+ public final ReentrantLock fileLock = new ReentrantLock(true);
+ public Path regionFile;
+
+ private final int compressionLevel;
+
+ public Path getRegionFile() {
+ return this.regionFile;
+ }
+
+ public ReentrantLock getFileLock() {
+ return this.fileLock;
+ }
+
+ public LinearRegionFile(Path file, int compression) throws IOException {
+ this.regionFile = file;
+ this.compressionLevel = compression;
+ File regionFile = new File(this.regionFile.toString());
+
+ LZ4Compressor compressor = LZ4Factory.fastestInstance().fastCompressor();
+
+ for (int i = 0; i < 32 * 32; i++)
+ this.bufferUncompressedSize[i] = 0;
+
+ if(regionFile.canRead()) {
+ long fileLength = file.toFile().length();
+ FileInputStream fileStream = new FileInputStream(regionFile);
+ DataInputStream rawDataStream = new DataInputStream(fileStream);
+
+ long SUPERBLOCK = -4323716122432332390L;
+ byte VERSION = 1;
+ int HEADER_SIZE = 32;
+ int FOOTER_SIZE = 8;
+
+ long superBlock = rawDataStream.readLong();
+
+ if (superBlock != SUPERBLOCK)
+ throw new RuntimeException("Superblock invalid: " + superBlock + " file " + file);
+
+ byte version = rawDataStream.readByte();
+
+ if (version != VERSION)
+ throw new RuntimeException("Version invalid: " + version + " file " + file);
+
+ rawDataStream.readLong(); // newestTimestamp
+ rawDataStream.readByte(); // Compression level
+ rawDataStream.readShort(); // Chunk count
+ int dataCount = rawDataStream.readInt();
+
+ if (fileLength != HEADER_SIZE + dataCount + FOOTER_SIZE)
+ throw new IOException("File length invalid " + this.regionFile + " " + fileLength + " " + (HEADER_SIZE + dataCount + FOOTER_SIZE));
+
+ rawDataStream.readLong(); // Data Hash
+ byte[] rawCompressed = new byte[dataCount];
+
+ rawDataStream.readFully(rawCompressed, 0, dataCount);
+
+ superBlock = rawDataStream.readLong();
+
+ if (superBlock != SUPERBLOCK) {
+ throw new IOException("Footer superblock invalid " + this.regionFile);
+ }
+
+ DataInputStream dataStream = new DataInputStream(new ZstdInputStream(new ByteArrayInputStream(rawCompressed)));
+
+ int[] starts = new int[32 * 32];
+ for(int i = 0 ; i < 32 * 32 ; i++) {
+ starts[i] = dataStream.readInt();
+ dataStream.readInt(); // Skip timestamps
+ }
+
+ for(int i = 0 ; i < 32 * 32 ; i++) {
+ if(starts[i] > 0) {
+ int size = starts[i];
+ byte[] b = new byte[size];
+ dataStream.readFully(b, 0, size);
+
+ int maxCompressedLength = compressor.maxCompressedLength(size);
+ byte[] compressed = new byte[maxCompressedLength];
+ int compressedLength = compressor.compress(b, 0, size, compressed, 0, maxCompressedLength);
+ b = new byte[compressedLength];
+ System.arraycopy(compressed, 0, b, 0, compressedLength);
+
+ this.buffer[i] = b;
+ this.bufferUncompressedSize[i] = size;
+ }
+ }
+ }
+ this.start();
+ }
+
+ private synchronized void markToSave() {
+ synchronized(markedToSaveLock) {
+ markedToSave = true;
+ }
+ }
+
+ private synchronized boolean isMarkedToSave() {
+ synchronized(markedToSaveLock) {
+ if(markedToSave) {
+ markedToSave = false;
+ return true;
+ }
+ return false;
+ }
+ }
+
+ public void run() {
+ try {
+ while(true) {
+ if(markedToSave) {
+ try {
+ flush();
+ } catch(IOException ex) {
+ LOGGER.error("Region file " + this.regionFile.toAbsolutePath() + " flush failed");
+ }
+ }
+ for(int i = 0 ; i < 100 ; i++) {
+ Thread.sleep(100);
+ if(close) {
+ return;
+ }
+ }
+ }
+ } catch(InterruptedException ignored) {}
+ }
+
+ public synchronized boolean doesChunkExist(ChunkPos pos) throws Exception {
+ throw new Exception("doesChunkExist is a stub");
+ }
+
+ public synchronized void flush() throws IOException {
+ if(!isMarkedToSave())
+ return;
+
+ long SUPERBLOCK = -4323716122432332390L;
+ byte VERSION = 1;
+ long timestamp = System.currentTimeMillis() / 1000L;
+ short chunkCount = 0;
+
+ File tempFile = new File(regionFile.toString() + ".tmp");
+ FileOutputStream fileStream = new FileOutputStream(tempFile);
+
+ ByteArrayOutputStream zstdByteArray = new ByteArrayOutputStream();
+ ZstdOutputStream zstdStream = new ZstdOutputStream(zstdByteArray, this.compressionLevel);
+ zstdStream.setChecksum(true);
+ DataOutputStream zstdDataStream = new DataOutputStream(zstdStream);
+ DataOutputStream dataStream = new DataOutputStream(fileStream);
+
+ dataStream.writeLong(SUPERBLOCK);
+ dataStream.writeByte(VERSION);
+ dataStream.writeLong(timestamp);
+ dataStream.writeByte(this.compressionLevel);
+
+ LZ4FastDecompressor decompressor = LZ4Factory.fastestInstance().fastDecompressor();
+
+ ArrayList<byte[]> byteBuffers = new ArrayList<>();
+ for(int i = 0 ; i < 32 * 32 ; i++) {
+ if(this.bufferUncompressedSize[i] != 0) {
+ chunkCount += 1;
+ byte[] content = new byte[bufferUncompressedSize[i]];
+ decompressor.decompress(buffer[i], 0, content, 0, bufferUncompressedSize[i]);
+
+ byteBuffers.add(content);
+ } else byteBuffers.add(null);
+ }
+ for(int i = 0 ; i < 32 * 32 ; i++) {
+ zstdDataStream.writeInt(this.bufferUncompressedSize[i]);
+ zstdDataStream.writeInt(0);
+ }
+ for(int i = 0 ; i < 32 * 32 ; i++) {
+ if(byteBuffers.get(i) != null)
+ zstdDataStream.write(byteBuffers.get(i), 0, byteBuffers.get(i).length);
+ }
+ zstdDataStream.close();
+
+ dataStream.writeShort(chunkCount);
+
+ byte[] compressed = zstdByteArray.toByteArray();
+
+ dataStream.writeInt(compressed.length);
+ dataStream.writeLong(XXHashFactory.fastestInstance().hash64().hash(compressed, 0, compressed.length, 0)); // TODO: Hash the contents, not the whole thing
+
+ dataStream.write(compressed, 0, compressed.length);
+ dataStream.writeLong(SUPERBLOCK);
+
+ dataStream.close();
+
+ fileStream.close();
+ Files.move(tempFile.toPath(), this.regionFile, StandardCopyOption.REPLACE_EXISTING);
+ }
+
+ public void setStatus(int x, int z, ChunkStatus status) {
+ this.statuses[getChunkIndex(x, z)] = status;
+ }
+
+ public synchronized void write(ChunkPos pos, ByteBuffer buffer) {
+ LZ4Compressor compressor = LZ4Factory.fastestInstance().fastCompressor();
+ try {
+ byte[] b = toByteArray(new ByteArrayInputStream(buffer.array()));
+ int uncompressedSize = b.length;
+
+ int maxCompressedLength = compressor.maxCompressedLength(b.length);
+ byte[] compressed = new byte[maxCompressedLength];
+ int compressedLength = compressor.compress(b, 0, b.length, compressed, 0, maxCompressedLength);
+ b = new byte[compressedLength];
+ System.arraycopy(compressed, 0, b, 0, compressedLength);
+
+ this.buffer[getChunkIndex(pos.x, pos.z)] = b;
+ this.bufferUncompressedSize[getChunkIndex(pos.x, pos.z)] = uncompressedSize;
+ } catch (IOException e) {
+ LOGGER.error("Chunk write IOException " + e + " " + this.regionFile);
+ }
+
+ markToSave();
+ }
+
+ public DataOutputStream getChunkDataOutputStream(ChunkPos pos) {
+ return new DataOutputStream(new BufferedOutputStream(new LinearRegionFile.ChunkBuffer(pos)));
+ }
+
+ private class ChunkBuffer extends ByteArrayOutputStream {
+
+ private final ChunkPos pos;
+
+ public ChunkBuffer(ChunkPos chunkcoordintpair) {
+ super();
+ this.pos = chunkcoordintpair;
+ }
+
+ public void close() throws IOException {
+ ByteBuffer bytebuffer = ByteBuffer.wrap(this.buf, 0, this.count);
+ LinearRegionFile.this.write(this.pos, bytebuffer);
+ }
+ }
+
+ private byte[] toByteArray(InputStream in) throws IOException {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ byte[] tempBuffer = new byte[4096];
+
+ int length;
+ while ((length = in.read(tempBuffer)) >= 0) {
+ out.write(tempBuffer, 0, length);
+ }
+
+ return out.toByteArray();
+ }
+
+ @Nullable
+ public synchronized DataInputStream getChunkDataInputStream(ChunkPos pos) {
+ if(this.bufferUncompressedSize[getChunkIndex(pos.x, pos.z)] != 0) {
+ LZ4FastDecompressor decompressor = LZ4Factory.fastestInstance().fastDecompressor();
+ byte[] content = new byte[bufferUncompressedSize[getChunkIndex(pos.x, pos.z)]];
+ decompressor.decompress(this.buffer[getChunkIndex(pos.x, pos.z)], 0, content, 0, bufferUncompressedSize[getChunkIndex(pos.x, pos.z)]);
+ return new DataInputStream(new ByteArrayInputStream(content));
+ }
+ return null;
+ }
+
+ public ChunkStatus getStatusIfCached(int x, int z) {
+ return this.statuses[getChunkIndex(x, z)];
+ }
+
+ public void clear(ChunkPos pos) {
+ int i = getChunkIndex(pos.x, pos.z);
+ this.buffer[i] = null;
+ this.bufferUncompressedSize[i] = 0;
+ markToSave();
+ }
+
+ public boolean hasChunk(ChunkPos pos) {
+ return this.bufferUncompressedSize[getChunkIndex(pos.x, pos.z)] > 0;
+ }
+
+ public void close() throws IOException {
+ close = true;
+ try {
+ flush();
+ } catch(IOException e) {
+ throw new IOException("Region flush IOException " + e + " " + this.regionFile);
+ }
+ }
+
+ private static int getChunkIndex(int x, int z) {
+ return (x & 31) + (z & 31) * 32;
+ }
+
+ public boolean recalculateHeader() {
+ return false;
+ }
+
+ public void setOversized(int x, int z, boolean something) {}
+
+ public CompoundTag getOversizedData(int x, int z) throws IOException {
+ throw new IOException("getOversizedData is a stub " + this.regionFile);
+ }
+
+ public boolean isOversized(int x, int z) {
+ return false;
+ }
+}
diff --git a/src/main/java/io/papermc/paper/chunk/system/io/RegionFileIOThread.java b/src/main/java/io/papermc/paper/chunk/system/io/RegionFileIOThread.java
index 8a11e10b01fa012b2f98b1c193c53251e848f909..61001e76b38f8647b33e73b5cc15b4b6785cf49a 100644
--- a/src/main/java/io/papermc/paper/chunk/system/io/RegionFileIOThread.java
+++ b/src/main/java/io/papermc/paper/chunk/system/io/RegionFileIOThread.java
@@ -811,7 +811,7 @@ public final class RegionFileIOThread extends PrioritisedQueueExecutorThread {
final ChunkDataController taskController) {
final ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ);
if (intendingToBlock) {
- return taskController.computeForRegionFile(chunkX, chunkZ, true, (final RegionFile file) -> {
+ return taskController.computeForRegionFile(chunkX, chunkZ, true, (final dev.kaiijumc.kaiiju.region.AbstractRegionFile file) -> { // Kaiiju
if (file == null) { // null if no regionfile exists
return Boolean.FALSE;
}
@@ -824,7 +824,7 @@ public final class RegionFileIOThread extends PrioritisedQueueExecutorThread {
return Boolean.FALSE;
} // else: it either exists or is not known, fall back to checking the loaded region file
- return taskController.computeForRegionFileIfLoaded(chunkX, chunkZ, (final RegionFile file) -> {
+ return taskController.computeForRegionFileIfLoaded(chunkX, chunkZ, (final dev.kaiijumc.kaiiju.region.AbstractRegionFile file) -> { // Kaiiju
if (file == null) { // null if not loaded
// not sure at this point, let the I/O thread figure it out
return Boolean.TRUE;
@@ -1126,9 +1126,9 @@ public final class RegionFileIOThread extends PrioritisedQueueExecutorThread {
return this.getCache().doesRegionFileNotExistNoIO(new ChunkPos(chunkX, chunkZ));
}
- public <T> T computeForRegionFile(final int chunkX, final int chunkZ, final boolean existingOnly, final Function<RegionFile, T> function) {
+ public <T> T computeForRegionFile(final int chunkX, final int chunkZ, final boolean existingOnly, final Function<dev.kaiijumc.kaiiju.region.AbstractRegionFile, T> function) { // Kaiiju
final RegionFileStorage cache = this.getCache();
- final RegionFile regionFile;
+ final dev.kaiijumc.kaiiju.region.AbstractRegionFile regionFile; // Kaiiju
synchronized (cache) {
try {
regionFile = cache.getRegionFile(new ChunkPos(chunkX, chunkZ), existingOnly, true);
@@ -1141,19 +1141,19 @@ public final class RegionFileIOThread extends PrioritisedQueueExecutorThread {
return function.apply(regionFile);
} finally {
if (regionFile != null) {
- regionFile.fileLock.unlock();
+ regionFile.getFileLock().unlock(); // Kaiiju
}
}
}
- public <T> T computeForRegionFileIfLoaded(final int chunkX, final int chunkZ, final Function<RegionFile, T> function) {
+ public <T> T computeForRegionFileIfLoaded(final int chunkX, final int chunkZ, final Function<dev.kaiijumc.kaiiju.region.AbstractRegionFile, T> function) { // Kaiiju
final RegionFileStorage cache = this.getCache();
- final RegionFile regionFile;
+ final dev.kaiijumc.kaiiju.region.AbstractRegionFile regionFile; // Kaiiju
synchronized (cache) {
regionFile = cache.getRegionFileIfLoaded(new ChunkPos(chunkX, chunkZ));
if (regionFile != null) {
- regionFile.fileLock.lock();
+ regionFile.getFileLock().lock(); // Kaiiju
}
}
@@ -1161,7 +1161,7 @@ public final class RegionFileIOThread extends PrioritisedQueueExecutorThread {
return function.apply(regionFile);
} finally {
if (regionFile != null) {
- regionFile.fileLock.unlock();
+ regionFile.getFileLock().unlock(); // Kaiiju
}
}
}
diff --git a/src/main/java/io/papermc/paper/world/ThreadedWorldUpgrader.java b/src/main/java/io/papermc/paper/world/ThreadedWorldUpgrader.java
index 513833c2ea23df5b079d157bc5cb89d5c9754c0b..a62a1b281bd0b6ad7d59b45b9470d84f496f6539 100644
--- a/src/main/java/io/papermc/paper/world/ThreadedWorldUpgrader.java
+++ b/src/main/java/io/papermc/paper/world/ThreadedWorldUpgrader.java
@@ -83,9 +83,13 @@ public class ThreadedWorldUpgrader {
}
LOGGER.info("Found " + regionFiles.length + " regionfiles to convert");
LOGGER.info("Starting conversion now for world " + this.worldName);
-
+ // Kaiiju start
+ String formatName = ((org.bukkit.craftbukkit.CraftWorld) org.bukkit.Bukkit.getWorld(worldName)).getHandle().kaiijuConfig.regionFormatName;
+ int linearCompression = ((org.bukkit.craftbukkit.CraftWorld) org.bukkit.Bukkit.getWorld(worldName)).getHandle().kaiijuConfig.regionFormatLinearCompressionLevel;
+ LOGGER.info("Using format " + formatName + " (" + linearCompression + ")");
+ // Kaiiju end
final WorldInfo info = new WorldInfo(() -> worldPersistentData,
- new ChunkStorage(regionFolder.toPath(), this.dataFixer, false), this.removeCaches, this.dimensionType, this.generatorKey);
+ new ChunkStorage(formatName, linearCompression, regionFolder.toPath(), this.dataFixer, false), this.removeCaches, this.dimensionType, this.generatorKey); // Kaiiju
long expectedChunks = (long)regionFiles.length * (32L * 32L);
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index 6f12d91f73e04c25fb0bc9054dc7d49de16e614a..43503974433c4d1808b2acb2b79f46d640725dc0 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -272,7 +272,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
// Paper end
public ChunkMap(ServerLevel world, LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, StructureTemplateManager structureTemplateManager, Executor executor, BlockableEventLoop<Runnable> mainThreadExecutor, LightChunkGetter chunkProvider, ChunkGenerator chunkGenerator, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkStatusChangeListener, Supplier<DimensionDataStorage> persistentStateManagerFactory, int viewDistance, boolean dsync) {
- super(session.getDimensionPath(world.dimension()).resolve("region"), dataFixer, dsync);
+ super(world.getLevel().kaiijuConfig.regionFormatName, world.getLevel().kaiijuConfig.regionFormatLinearCompressionLevel, session.getDimensionPath(world.dimension()).resolve("region"), dataFixer, dsync); // Kaiiju
// Paper - rewrite chunk system
this.tickingGenerated = new AtomicInteger();
//this.playerMap = new PlayerMap(); // Folia - region threading
@@ -317,7 +317,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.lightEngine = new ThreadedLevelLightEngine(chunkProvider, this, this.level.dimensionType().hasSkyLight(), null, null); // Paper - rewrite chunk system
this.distanceManager = new ChunkMap.ChunkDistanceManager(executor, mainThreadExecutor);
this.overworldDataStorage = persistentStateManagerFactory;
- this.poiManager = new PoiManager(path.resolve("poi"), dataFixer, dsync, iregistrycustom, world);
+ this.poiManager = new PoiManager(this.level.kaiijuConfig.regionFormatName, this.level.kaiijuConfig.regionFormatLinearCompressionLevel, path.resolve("poi"), dataFixer, dsync, iregistrycustom, world); // Kaiiju
this.setViewDistance(viewDistance);
// Paper start
this.dataRegionManager = new io.papermc.paper.chunk.SingleThreadChunkRegionManager(this.level, 2, (1.0 / 3.0), 1, 6, "Data", DataRegionData::new, DataRegionSectionData::new);
@@ -849,13 +849,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
// Paper start - chunk status cache "api"
public ChunkStatus getChunkStatusOnDiskIfCached(ChunkPos chunkPos) {
- net.minecraft.world.level.chunk.storage.RegionFile regionFile = regionFileCache.getRegionFileIfLoaded(chunkPos);
+ dev.kaiijumc.kaiiju.region.AbstractRegionFile regionFile = regionFileCache.getRegionFileIfLoaded(chunkPos); // Kaiiju
return regionFile == null ? null : regionFile.getStatusIfCached(chunkPos.x, chunkPos.z);
}
public ChunkStatus getChunkStatusOnDisk(ChunkPos chunkPos) throws IOException {
- net.minecraft.world.level.chunk.storage.RegionFile regionFile = regionFileCache.getRegionFile(chunkPos, true);
+ dev.kaiijumc.kaiiju.region.AbstractRegionFile regionFile = regionFileCache.getRegionFile(chunkPos, true); // Kaiiju
if (regionFile == null || !regionFileCache.chunkExists(chunkPos)) {
return null;
@@ -873,7 +873,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
public void updateChunkStatusOnDisk(ChunkPos chunkPos, @Nullable CompoundTag compound) throws IOException {
- net.minecraft.world.level.chunk.storage.RegionFile regionFile = regionFileCache.getRegionFile(chunkPos, false);
+ dev.kaiijumc.kaiiju.region.AbstractRegionFile regionFile = regionFileCache.getRegionFile(chunkPos, false); // Kaiiju
regionFile.setStatus(chunkPos.x, chunkPos.z, ChunkSerializer.getStatus(compound));
}
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
index acc8af33ad8534d812908b0feb9a1963ee2c64fb..20359c63b19c7e0c703ef1562fb774803d631c41 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -425,9 +425,8 @@ public class ServerLevel extends Level implements WorldGenLevel {
private final EntityRegionFileStorage entityStorage;
private static final class EntityRegionFileStorage extends net.minecraft.world.level.chunk.storage.RegionFileStorage {
-
- public EntityRegionFileStorage(Path directory, boolean dsync) {
- super(directory, dsync);
+ public EntityRegionFileStorage(String format, int linearCompression, Path directory, boolean dsync) { // Kaiiju
+ super(format, linearCompression, directory, dsync); // Kaiiju
}
protected void write(ChunkPos pos, net.minecraft.nbt.CompoundTag nbt) throws IOException {
@@ -633,7 +632,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
// CraftBukkit end
boolean flag2 = minecraftserver.forceSynchronousWrites();
DataFixer datafixer = minecraftserver.getFixerUpper();
- this.entityStorage = new EntityRegionFileStorage(convertable_conversionsession.getDimensionPath(resourcekey).resolve("entities"), flag2); // Paper - rewrite chunk system //EntityPersistentStorage<Entity> entitypersistentstorage = new EntityStorage(this, convertable_conversionsession.getDimensionPath(resourcekey).resolve("entities"), datafixer, flag2, minecraftserver);
+ this.entityStorage = new EntityRegionFileStorage(this.getLevel().kaiijuConfig.regionFormatName, this.getLevel().kaiijuConfig.regionFormatLinearCompressionLevel, convertable_conversionsession.getDimensionPath(resourcekey).resolve("entities"), flag2); // Paper - rewrite chunk system //EntityPersistentStorage<Entity> entitypersistentstorage = new EntityStorage(this, convertable_conversionsession.getDimensionPath(resourcekey).resolve("entities"), datafixer, flag2, minecraftserver); // Kaiiju
// this.entityManager = new PersistentEntitySectionManager<>(Entity.class, new ServerLevel.EntityCallbacks(), entitypersistentstorage, this.entitySliceManager); // Paper // Paper - rewrite chunk system
StructureTemplateManager structuretemplatemanager = minecraftserver.getStructureManager();
diff --git a/src/main/java/net/minecraft/util/worldupdate/WorldUpgrader.java b/src/main/java/net/minecraft/util/worldupdate/WorldUpgrader.java
index e0bfeebeaac1aaea64bc07cdfdf7790e3e43ca7b..469802b9c454c5e98c0a55ba89559d9d8ba0c4c0 100644
--- a/src/main/java/net/minecraft/util/worldupdate/WorldUpgrader.java
+++ b/src/main/java/net/minecraft/util/worldupdate/WorldUpgrader.java
@@ -61,7 +61,7 @@ public class WorldUpgrader {
private volatile int skipped;
private final Object2FloatMap<ResourceKey<LevelStem>> progressMap = Object2FloatMaps.synchronize(new Object2FloatOpenCustomHashMap(Util.identityStrategy())); // CraftBukkit
private volatile Component status = Component.translatable("optimizeWorld.stage.counting");
- public static final Pattern REGEX = Pattern.compile("^r\\.(-?[0-9]+)\\.(-?[0-9]+)\\.mca$");
+ public static Pattern REGEX = Pattern.compile("^r\\.(-?[0-9]+)\\.(-?[0-9]+)\\.(linear | mca)$"); // Kaiiju
private final DimensionDataStorage overworldDataStorage;
public WorldUpgrader(LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, Registry<LevelStem> dimensionOptionsRegistry, boolean eraseCache) {
@@ -115,8 +115,12 @@ public class WorldUpgrader {
while (iterator1.hasNext()) {
ResourceKey<LevelStem> resourcekey1 = (ResourceKey) iterator1.next(); // CraftBukkit
Path path = this.levelStorage.getDimensionPath((ResourceKey) null); // CraftBukkit
-
- builder1.put(resourcekey1, new ChunkStorage(path.resolve("region"), this.dataFixer, true));
+ // Kaiiju start
+ String worldName = this.levelStorage.getLevelId();
+ String formatName = ((org.bukkit.craftbukkit.CraftWorld) org.bukkit.Bukkit.getWorld(worldName)).getHandle().kaiijuConfig.regionFormatName;
+ int linearCompression = ((org.bukkit.craftbukkit.CraftWorld) org.bukkit.Bukkit.getWorld(worldName)).getHandle().kaiijuConfig.regionFormatLinearCompressionLevel;
+ builder1.put(resourcekey1, new ChunkStorage(formatName, linearCompression, path.resolve("region"), this.dataFixer, true));
+ // Kaiiju end
}
ImmutableMap<ResourceKey<LevelStem>, ChunkStorage> immutablemap1 = builder1.build(); // CraftBukkit
@@ -235,7 +239,7 @@ public class WorldUpgrader {
File file = this.levelStorage.getDimensionPath((ResourceKey) null).toFile(); // CraftBukkit
File file1 = new File(file, "region");
File[] afile = file1.listFiles((file2, s) -> {
- return s.endsWith(".mca");
+ return s.endsWith(".mca") || s.endsWith(".linear"); // Kaiiju
});
if (afile == null) {
@@ -254,7 +258,11 @@ public class WorldUpgrader {
int l = Integer.parseInt(matcher.group(2)) << 5;
try {
- RegionFile regionfile = new RegionFile(file2.toPath(), file1.toPath(), true);
+ // Kaiiju start
+ String worldName = this.levelStorage.getLevelId();
+ int linearCompression = ((org.bukkit.craftbukkit.CraftWorld) org.bukkit.Bukkit.getWorld(worldName)).getHandle().kaiijuConfig.regionFormatLinearCompressionLevel;
+ dev.kaiijumc.kaiiju.region.AbstractRegionFile regionfile = dev.kaiijumc.kaiiju.region.AbstractRegionFileFactory.getAbstractRegionFile(linearCompression, file2.toPath(), file1.toPath(), true);
+ // Kaiiju end
try {
for (int i1 = 0; i1 < 32; ++i1) {
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
index 9c56304476b4fc841b5d7694232617586ebd8e84..f560aa13c6c8ffecb456f478687dc6a9eb5e8017 100644
--- 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
@@ -59,8 +59,8 @@ public class PoiManager extends SectionStorage<PoiSection> {
// Paper end - rewrite chunk system
- public PoiManager(Path path, DataFixer dataFixer, boolean dsync, RegistryAccess registryManager, LevelHeightAccessor world) {
- super(path, PoiSection::codec, PoiSection::new, dataFixer, DataFixTypes.POI_CHUNK, dsync, registryManager, world);
+ public PoiManager(String formatName, int linearCompression, Path path, DataFixer dataFixer, boolean dsync, RegistryAccess registryManager, LevelHeightAccessor world) { // Kaiiju
+ super(formatName, linearCompression, path, PoiSection::codec, PoiSection::new, dataFixer, DataFixTypes.POI_CHUNK, dsync, registryManager, world); // Kaiiju
this.world = (net.minecraft.server.level.ServerLevel)world; // Paper - rewrite chunk system
}
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
index 29facbdcbad17ce38bf785f7f3f8346d81cbc32f..52c2e0fe73a5af18535a2b0b9a506919e3f93003 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
@@ -37,11 +37,12 @@ public class ChunkStorage implements AutoCloseable {
public final RegionFileStorage regionFileCache;
// Paper end - async chunk loading
- public ChunkStorage(Path directory, DataFixer dataFixer, boolean dsync) {
+ public ChunkStorage(String format, int linearCompression, Path directory, DataFixer dataFixer, boolean dsync) { // Kaiiju
this.fixerUpper = dataFixer;
+
// Paper start - async chunk io
// remove IO worker
- this.regionFileCache = new RegionFileStorage(directory, dsync, true); // Paper - nuke IOWorker // Paper
+ this.regionFileCache = new RegionFileStorage(format, linearCompression, directory, dsync, true); // Paper - nuke IOWorker // Paper
// Paper end - async chunk io
}
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 dcfe090c269d4cbcc2eb1b6f85392848bb34656c..d42c320179ae055b8675d1ce6ce1788ecafb8e9d 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
@@ -27,7 +27,7 @@ import net.minecraft.nbt.NbtIo;
import net.minecraft.world.level.ChunkPos;
import org.slf4j.Logger;
-public class RegionFile implements AutoCloseable {
+public class RegionFile implements AutoCloseable, dev.kaiijumc.kaiiju.region.AbstractRegionFile { // Kaiiju
private static final Logger LOGGER = LogUtils.getLogger();
private static final int SECTOR_BYTES = 4096;
@@ -51,6 +51,16 @@ public class RegionFile implements AutoCloseable {
public final java.util.concurrent.locks.ReentrantLock fileLock = new java.util.concurrent.locks.ReentrantLock(true); // Paper
public final Path regionFile; // Paper
+ // Kaiiju start - Abstract getters
+ public Path getRegionFile() {
+ return this.regionFile;
+ }
+
+ public java.util.concurrent.locks.ReentrantLock getFileLock() {
+ return this.fileLock;
+ }
+ // Kaiiju end
+
// Paper start - try to recover from RegionFile header corruption
private static long roundToSectors(long bytes) {
long sectors = bytes >>> 12; // 4096 = 2^12
@@ -129,7 +139,7 @@ public class RegionFile implements AutoCloseable {
}
// note: only call for CHUNK regionfiles
- boolean recalculateHeader() throws IOException {
+ public boolean recalculateHeader() throws IOException { // Kaiiju
if (!this.canRecalcHeader) {
return false;
}
@@ -955,10 +965,10 @@ public class RegionFile implements AutoCloseable {
private static int getChunkIndex(int x, int z) {
return (x & 31) + (z & 31) * 32;
}
- synchronized boolean isOversized(int x, int z) {
+ public synchronized boolean isOversized(int x, int z) { // Kaiiju
return this.oversized[getChunkIndex(x, z)] == 1;
}
- synchronized void setOversized(int x, int z, boolean oversized) throws IOException {
+ public synchronized void setOversized(int x, int z, boolean oversized) throws IOException { // Kaiiju
final int offset = getChunkIndex(x, z);
boolean previous = this.oversized[offset] == 1;
this.oversized[offset] = (byte) (oversized ? 1 : 0);
@@ -997,7 +1007,7 @@ public class RegionFile implements AutoCloseable {
return this.regionFile.getParent().resolve(this.regionFile.getFileName().toString().replaceAll("\\.mca$", "") + "_oversized_" + x + "_" + z + ".nbt");
}
- synchronized CompoundTag getOversizedData(int x, int z) throws IOException {
+ public synchronized CompoundTag getOversizedData(int x, int z) throws IOException { // Kaiiju
Path file = getOversizedFile(x, z);
try (DataInputStream out = new DataInputStream(new java.io.BufferedInputStream(new InflaterInputStream(Files.newInputStream(file))))) {
return NbtIo.read((java.io.DataInput) out);
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 9633b01d2d961fd1403e353484d336376ef009eb..c9fbba6f709ed0540dcc22b41e50ce9d01a6381c 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
@@ -22,9 +22,13 @@ public class RegionFileStorage implements AutoCloseable {
public static final String ANVIL_EXTENSION = ".mca";
private static final int MAX_CACHE_SIZE = 256;
- public final Long2ObjectLinkedOpenHashMap<RegionFile> regionCache = new Long2ObjectLinkedOpenHashMap();
+ public final Long2ObjectLinkedOpenHashMap<dev.kaiijumc.kaiiju.region.AbstractRegionFile> regionCache = new Long2ObjectLinkedOpenHashMap(); // Kaiiju
private final Path folder;
private final boolean sync;
+ // Kaiiju start - Per world chunk format
+ public final String format;
+ public final int linearCompression;
+ // Kaiiju end
private final boolean isChunkData; // Paper
@@ -57,11 +61,15 @@ public class RegionFileStorage implements AutoCloseable {
}
// Paper end - cache regionfile does not exist state
- protected RegionFileStorage(Path directory, boolean dsync) { // Paper - protected constructor
+ protected RegionFileStorage(String format, int linearCompression, Path directory, boolean dsync) { // Paper - protected constructor
// Paper start - add isChunkData param
- this(directory, dsync, false);
+ this(format, linearCompression, directory, dsync, false);
}
- RegionFileStorage(Path directory, boolean dsync, boolean isChunkData) {
+ RegionFileStorage(String format, int linearCompression, Path directory, boolean dsync, boolean isChunkData) { // Kaiiju
+ // Kaiiju start
+ this.format = format;
+ this.linearCompression = linearCompression;
+ // Kaiiju end
this.isChunkData = isChunkData;
// Paper end - add isChunkData param
this.folder = directory;
@@ -71,7 +79,7 @@ public class RegionFileStorage implements AutoCloseable {
// Paper start
public static @Nullable ChunkPos getRegionFileCoordinates(Path file) {
String fileName = file.getFileName().toString();
- if (!fileName.startsWith("r.") || !fileName.endsWith(".mca")) {
+ if (!fileName.startsWith("r.") || !fileName.endsWith(".mca") || !fileName.endsWith(".linear")) { // Kaiiju
return null;
}
@@ -91,29 +99,29 @@ public class RegionFileStorage implements AutoCloseable {
}
}
- public synchronized RegionFile getRegionFileIfLoaded(ChunkPos chunkcoordintpair) {
+ public synchronized dev.kaiijumc.kaiiju.region.AbstractRegionFile getRegionFileIfLoaded(ChunkPos chunkcoordintpair) { // Kaiiju
return this.regionCache.getAndMoveToFirst(ChunkPos.asLong(chunkcoordintpair.getRegionX(), chunkcoordintpair.getRegionZ()));
}
public synchronized boolean chunkExists(ChunkPos pos) throws IOException {
- RegionFile regionfile = getRegionFile(pos, true);
+ dev.kaiijumc.kaiiju.region.AbstractRegionFile regionfile = getRegionFile(pos, true); // Kaiiju
return regionfile != null ? regionfile.hasChunk(pos) : false;
}
- public synchronized RegionFile getRegionFile(ChunkPos chunkcoordintpair, boolean existingOnly) throws IOException { // CraftBukkit
+ public synchronized dev.kaiijumc.kaiiju.region.AbstractRegionFile getRegionFile(ChunkPos chunkcoordintpair, boolean existingOnly) throws IOException { // CraftBukkit // Kaiiju
return this.getRegionFile(chunkcoordintpair, existingOnly, false);
}
- public synchronized RegionFile getRegionFile(ChunkPos chunkcoordintpair, boolean existingOnly, boolean lock) throws IOException {
+ public synchronized dev.kaiijumc.kaiiju.region.AbstractRegionFile getRegionFile(ChunkPos chunkcoordintpair, boolean existingOnly, boolean lock) throws IOException { // Kaiiju
// Paper end
long i = ChunkPos.asLong(chunkcoordintpair.getRegionX(), chunkcoordintpair.getRegionZ()); final long regionPos = i; // Paper - OBFHELPER
- RegionFile regionfile = (RegionFile) this.regionCache.getAndMoveToFirst(i);
+ dev.kaiijumc.kaiiju.region.AbstractRegionFile regionfile = this.regionCache.getAndMoveToFirst(i); // Kaiiju
if (regionfile != null) {
// Paper start
if (lock) {
// must be in this synchronized block
- regionfile.fileLock.lock();
+ regionfile.getFileLock().lock(); // Kaiiju
}
// Paper end
return regionfile;
@@ -124,28 +132,46 @@ public class RegionFileStorage implements AutoCloseable {
}
// Paper end - cache regionfile does not exist state
if (this.regionCache.size() >= io.papermc.paper.configuration.GlobalConfiguration.get().misc.regionFileCacheSize) { // Paper - configurable
- ((RegionFile) this.regionCache.removeLast()).close();
+ this.regionCache.removeLast().close(); // Kaiiju
}
// Paper - only create directory if not existing only - moved down
Path path = this.folder;
int j = chunkcoordintpair.getRegionX();
- Path path1 = path.resolve("r." + j + "." + chunkcoordintpair.getRegionZ() + ".mca"); // Paper - diff on change
- if (existingOnly && !java.nio.file.Files.exists(path1)) { // Paper start - cache regionfile does not exist state
- this.markNonExisting(regionPos);
- return null; // CraftBukkit
+ // Kaiiju start - Polyglot
+ //Path path1 = path.resolve("r." + j + "." + chunkcoordintpair.getRegionZ() + ".mca"); // Paper - diff on change
+ Path path1;
+ if (existingOnly) {
+ Path anvil = path.resolve("r." + j + "." + chunkcoordintpair.getRegionZ() + ".mca");
+ Path linear = path.resolve("r." + j + "." + chunkcoordintpair.getRegionZ() + ".linear");
+ if (java.nio.file.Files.exists(anvil)) path1 = anvil;
+ else if (java.nio.file.Files.exists(linear)) path1 = linear;
+ else {
+ this.markNonExisting(regionPos);
+ return null;
+ }
} else {
+ String extension = switch (this.format) {
+ case "LINEAR" -> "linear";
+ default -> "mca";
+ };
+ path1 = path.resolve("r." + j + "." + chunkcoordintpair.getRegionZ() + "." + extension);
this.createRegionFile(regionPos);
}
+ if (dev.kaiijumc.kaiiju.KaiijuConfig.regionFormatDebug)
+ org.bukkit.Bukkit.getLogger().info("Opening file " + path1 + " with format " + this.format + " (existingOnly = " + existingOnly + ")");
+
// Paper end - cache regionfile does not exist state
FileUtil.createDirectoriesSafe(this.folder); // Paper - only create directory if not existing only - moved from above
- RegionFile regionfile1 = new RegionFile(path1, this.folder, this.sync, this.isChunkData); // Paper - allow for chunk regionfiles to regen header
+ // Kaiiju end
+
+ dev.kaiijumc.kaiiju.region.AbstractRegionFile regionfile1 = dev.kaiijumc.kaiiju.region.AbstractRegionFileFactory.getAbstractRegionFile(this.linearCompression, path1, this.folder, this.sync, this.isChunkData); // Paper - allow for chunk regionfiles to regen header // Kaiiju
this.regionCache.putAndMoveToFirst(i, regionfile1);
// Paper start
if (lock) {
// must be in this synchronized block
- regionfile1.fileLock.lock();
+ regionfile1.getFileLock().lock(); // Kaiiju
}
// Paper end
return regionfile1;
@@ -173,7 +199,7 @@ public class RegionFileStorage implements AutoCloseable {
}
- private static CompoundTag readOversizedChunk(RegionFile regionfile, ChunkPos chunkCoordinate) throws IOException {
+ private static CompoundTag readOversizedChunk(dev.kaiijumc.kaiiju.region.AbstractRegionFile regionfile, ChunkPos chunkCoordinate) throws IOException { // Kaiiju
synchronized (regionfile) {
try (DataInputStream datainputstream = regionfile.getChunkDataInputStream(chunkCoordinate)) {
CompoundTag oversizedData = regionfile.getOversizedData(chunkCoordinate.x, chunkCoordinate.z);
@@ -220,14 +246,14 @@ public class RegionFileStorage implements AutoCloseable {
@Nullable
public CompoundTag read(ChunkPos pos) throws IOException {
// 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, true); // Paper
+ dev.kaiijumc.kaiiju.region.AbstractRegionFile regionfile = this.getRegionFile(pos, true, true); // Paper // Kaiiju
if (regionfile == null) {
return null;
}
// Paper start - Add regionfile parameter
return this.read(pos, regionfile);
}
- public CompoundTag read(ChunkPos pos, RegionFile regionfile) throws IOException {
+ public CompoundTag read(ChunkPos pos, dev.kaiijumc.kaiiju.region.AbstractRegionFile regionfile) throws IOException { // Kaiiju
// We add the regionfile parameter to avoid the potential deadlock (on fileLock) if we went back to obtain a regionfile
// if we decide to re-read
// Paper end
@@ -237,7 +263,7 @@ public class RegionFileStorage implements AutoCloseable {
// Paper start
if (regionfile.isOversized(pos.x, pos.z)) {
- printOversizedLog("Loading Oversized Chunk!", regionfile.regionFile, pos.x, pos.z);
+ printOversizedLog("Loading Oversized Chunk!", regionfile.getRegionFile(), pos.x, pos.z); // Kaiiju
return readOversizedChunk(regionfile, pos);
}
// Paper end
@@ -251,12 +277,12 @@ public class RegionFileStorage implements AutoCloseable {
if (this.isChunkData) {
ChunkPos chunkPos = ChunkSerializer.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.regionFile.toAbsolutePath());
+ 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.getRegionFile().toAbsolutePath()); // Kaiiju
if (regionfile.recalculateHeader()) {
- regionfile.fileLock.lock(); // otherwise we will unlock twice and only lock once.
+ regionfile.getFileLock().lock(); // otherwise we will unlock twice and only lock once. // Kaiiju
return this.read(pos, regionfile);
}
- net.minecraft.server.MinecraftServer.LOGGER.error("Can't recalculate regionfile header, regenerating chunk " + pos + " for " + regionfile.regionFile.toAbsolutePath());
+ net.minecraft.server.MinecraftServer.LOGGER.error("Can't recalculate regionfile header, regenerating chunk " + pos + " for " + regionfile.getRegionFile().toAbsolutePath()); // Kaiiju
return null;
}
}
@@ -290,13 +316,13 @@ public class RegionFileStorage implements AutoCloseable {
return nbttagcompound;
} finally { // Paper start
- regionfile.fileLock.unlock();
+ regionfile.getFileLock().unlock(); // Kaiiju
} // Paper end
}
public void scanChunk(ChunkPos chunkPos, StreamTagVisitor scanner) throws IOException {
// 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);
+ dev.kaiijumc.kaiiju.region.AbstractRegionFile regionfile = this.getRegionFile(chunkPos, true); // Kaiiju
if (regionfile == null) {
return;
}
@@ -326,7 +352,7 @@ public class RegionFileStorage implements AutoCloseable {
}
protected void write(ChunkPos pos, @Nullable CompoundTag nbt) throws IOException {
- RegionFile regionfile = this.getRegionFile(pos, nbt == null, true); // CraftBukkit // Paper // Paper start - rewrite chunk system
+ dev.kaiijumc.kaiiju.region.AbstractRegionFile regionfile = this.getRegionFile(pos, nbt == null, true); // CraftBukkit // Paper // Paper start - rewrite chunk system // Kaiiju
if (nbt == null && regionfile == null) {
return;
}
@@ -376,7 +402,7 @@ public class RegionFileStorage implements AutoCloseable {
}
// Paper end
} finally { // Paper start
- regionfile.fileLock.unlock();
+ regionfile.getFileLock().unlock(); // Kaiiju
} // Paper end
}
@@ -385,7 +411,7 @@ public class RegionFileStorage implements AutoCloseable {
ObjectIterator objectiterator = this.regionCache.values().iterator();
while (objectiterator.hasNext()) {
- RegionFile regionfile = (RegionFile) objectiterator.next();
+ dev.kaiijumc.kaiiju.region.AbstractRegionFile regionfile = (dev.kaiijumc.kaiiju.region.AbstractRegionFile) objectiterator.next(); // Kaiiju
try {
regionfile.close();
@@ -401,7 +427,7 @@ public class RegionFileStorage implements AutoCloseable {
ObjectIterator objectiterator = this.regionCache.values().iterator();
while (objectiterator.hasNext()) {
- RegionFile regionfile = (RegionFile) objectiterator.next();
+ dev.kaiijumc.kaiiju.region.AbstractRegionFile regionfile = (dev.kaiijumc.kaiiju.region.AbstractRegionFile) objectiterator.next(); // Kaiiju
regionfile.flush();
}
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
index 5561b8499a0503b850974b1dc309edfb80219549..414aa3ae00c2d28e754235e3e93b6b623d4fd180 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java
@@ -47,8 +47,8 @@ public class SectionStorage<R> extends RegionFileStorage implements AutoCloseabl
public final RegistryAccess registryAccess; // Paper - rewrite chunk system
protected final LevelHeightAccessor levelHeightAccessor;
- public SectionStorage(Path path, Function<Runnable, Codec<R>> codecFactory, Function<Runnable, R> factory, DataFixer dataFixer, DataFixTypes dataFixTypes, boolean dsync, RegistryAccess dynamicRegistryManager, LevelHeightAccessor world) {
- super(path, dsync); // Paper - remove mojang I/O thread
+ public SectionStorage(String format, int linearCompression, Path path, Function<Runnable, Codec<R>> codecFactory, Function<Runnable, R> factory, DataFixer dataFixer, DataFixTypes dataFixTypes, boolean dsync, RegistryAccess dynamicRegistryManager, LevelHeightAccessor world) { // Kaiiju
+ super(format, linearCompression, path, dsync); // Paper - remove mojang I/O thread // Kaiiju
this.codec = codecFactory;
this.factory = factory;
this.fixerUpper = dataFixer;
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
index 2ead9af36c4d41c61b68a960ff17e25894efeb2a..cba3411386b7a9f596017d021d08f30c78f0c52b 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
@@ -554,7 +554,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
return true;
}
- net.minecraft.world.level.chunk.storage.RegionFile file;
+ dev.kaiijumc.kaiiju.region.AbstractRegionFile file; // Kaiiju
try {
file = world.getChunkSource().chunkMap.regionFileCache.getRegionFile(chunkPos, false);
} catch (java.io.IOException ex) {