1008 lines
56 KiB
Diff
1008 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 abfd4d5faf23511284b4efac771ef79da0ef72da..0a6549d0f545e81ae95527797333f2e2da85e1f1 100644
|
|
--- a/build.gradle.kts
|
|
+++ b/build.gradle.kts
|
|
@@ -13,6 +13,10 @@ dependencies {
|
|
exclude("io.papermc.paper", "paper-api")
|
|
}
|
|
// Pufferfish 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..67991815a0ff9235a846ca8db817c90f094c4a58
|
|
--- /dev/null
|
|
+++ b/src/main/java/dev/kaiijumc/kaiiju/region/AbstractRegionFileFactory.java
|
|
@@ -0,0 +1,30 @@
|
|
+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, int linearInternalCompression, Path file, Path directory, boolean dsync) throws IOException {
|
|
+ return getAbstractRegionFile(linearCompression, linearInternalCompression, file, directory, RegionFileVersion.VERSION_DEFLATE, dsync);
|
|
+ }
|
|
+
|
|
+ public static AbstractRegionFile getAbstractRegionFile(int linearCompression, int linearInternalCompression, Path file, Path directory, boolean dsync, boolean canRecalcHeader) throws IOException {
|
|
+ return getAbstractRegionFile(linearCompression, linearInternalCompression, file, directory, RegionFileVersion.VERSION_DEFLATE, dsync, canRecalcHeader);
|
|
+ }
|
|
+
|
|
+ public static AbstractRegionFile getAbstractRegionFile(int linearCompression, int linearInternalCompression, Path file, Path directory, RegionFileVersion outputChunkStreamVersion, boolean dsync) throws IOException {
|
|
+ return getAbstractRegionFile(linearCompression, linearInternalCompression, file, directory, outputChunkStreamVersion, dsync, false);
|
|
+ }
|
|
+
|
|
+ public static AbstractRegionFile getAbstractRegionFile(int linearCompression, int linearInternalCompression, Path file, Path directory, RegionFileVersion outputChunkStreamVersion, boolean dsync, boolean canRecalcHeader) throws IOException {
|
|
+ if (file.toString().endsWith(".linear")) {
|
|
+ return new LinearRegionFile(file, linearCompression, linearInternalCompression);
|
|
+ } 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..df2edc187480aef6af1cbeb82824984e31672068
|
|
--- /dev/null
|
|
+++ b/src/main/java/dev/kaiijumc/kaiiju/region/LinearRegionFile.java
|
|
@@ -0,0 +1,325 @@
|
|
+package dev.kaiijumc.kaiiju.region;
|
|
+
|
|
+import com.github.luben.zstd.Zstd;import com.github.luben.zstd.ZstdInputStream;
|
|
+import com.github.luben.zstd.ZstdOutputStream;
|
|
+import com.mojang.logging.LogUtils;
|
|
+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.Arrays;
|
|
+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;
|
|
+ private final int internalCompressionLevel;
|
|
+
|
|
+ public Path getRegionFile() {
|
|
+ return this.regionFile;
|
|
+ }
|
|
+
|
|
+ public ReentrantLock getFileLock() {
|
|
+ return this.fileLock;
|
|
+ }
|
|
+
|
|
+ public LinearRegionFile(Path file, int compression, int internalCompression) throws IOException {
|
|
+ this.regionFile = file;
|
|
+ this.compressionLevel = compression;
|
|
+ this.internalCompressionLevel = internalCompression;
|
|
+ File regionFile = new File(this.regionFile.toString());
|
|
+
|
|
+ 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 IOException("SUPERBLOCK VERSION! " + file);
|
|
+ }
|
|
+
|
|
+ byte version = rawDataStream.readByte();
|
|
+
|
|
+ if (version != VERSION) {
|
|
+ throw new IOException("INVALID VERSION! " + 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 = (int)Zstd.compressBound(size);
|
|
+ byte[] compressed = new byte[maxCompressedLength];
|
|
+ long compressedLength = Zstd.compress(compressed, b, this.internalCompressionLevel);
|
|
+ b = Arrays.copyOfRange(compressed, 0, (int) 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", ex);
|
|
+ }
|
|
+ }
|
|
+ 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);
|
|
+
|
|
+ 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]];
|
|
+ Zstd.decompress(content, buffer[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) {
|
|
+ try {
|
|
+ byte[] b = toByteArray(new ByteArrayInputStream(buffer.array()));
|
|
+ int uncompressedSize = b.length;
|
|
+ byte[] compressed = Zstd.compress(b, this.internalCompressionLevel);
|
|
+ this.buffer[getChunkIndex(pos.x, pos.z)] = compressed;
|
|
+ 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) {
|
|
+ byte[] compressedData = this.buffer[getChunkIndex(pos.x, pos.z)];
|
|
+ byte[] content = new byte[bufferUncompressedSize[getChunkIndex(pos.x, pos.z)]];
|
|
+ Zstd.decompress(content, compressedData);
|
|
+ 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 a08cde4eefe879adcee7c4118bc38f98c5097ed0..1cfc20b7496f93aff1d6c2387dc5bb8bacab8dc8 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;
|
|
}
|
|
@@ -819,7 +819,7 @@ public final class RegionFileIOThread extends PrioritisedQueueExecutorThread {
|
|
return file.hasChunk(chunkPos) ? Boolean.TRUE : Boolean.FALSE;
|
|
});
|
|
} else {
|
|
- 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
|
|
return Boolean.TRUE;
|
|
}
|
|
@@ -1116,9 +1116,9 @@ public final class RegionFileIOThread extends PrioritisedQueueExecutorThread {
|
|
return !this.tasks.isEmpty();
|
|
}
|
|
|
|
- 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);
|
|
@@ -1131,19 +1131,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
|
|
}
|
|
}
|
|
|
|
@@ -1151,7 +1151,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 95cac7edae8ac64811fc6a2f6b97dd4a0fceb0b0..e340878efb93499acd7fdf83a66ead9b53210a76 100644
|
|
--- a/src/main/java/io/papermc/paper/world/ThreadedWorldUpgrader.java
|
|
+++ b/src/main/java/io/papermc/paper/world/ThreadedWorldUpgrader.java
|
|
@@ -83,9 +83,15 @@ public class ThreadedWorldUpgrader {
|
|
}
|
|
LOGGER.info("Found " + regionFiles.length + " regionfiles to convert");
|
|
LOGGER.info("Starting conversion now for world " + this.worldName);
|
|
-
|
|
+ // Kaiiju start
|
|
+ net.minecraft.server.level.ServerLevel level = ((org.bukkit.craftbukkit.CraftWorld) org.bukkit.Bukkit.getWorld(worldName)).getHandle();
|
|
+ String formatName = level.kaiijuConfig.regionFormatName;
|
|
+ int linearCompression = level.kaiijuConfig.regionFormatLinearCompressionLevel;
|
|
+ int internalLinearCompression = level.kaiijuConfig.regionFormatLinearInternalCompressionLevel;
|
|
+ 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, internalLinearCompression, 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 75965afd7b4bed23a5ecf618c7f91ff5e7ffd92f..3dd88803c5897ac856c107e7f54c3b73cba5adf8 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
@@ -291,7 +291,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, world.getLevel().kaiijuConfig.regionFormatLinearInternalCompressionLevel, session.getDimensionPath(world.dimension()).resolve("region"), dataFixer, dsync); // Kaiiju
|
|
// Paper - rewrite chunk system
|
|
this.tickingGenerated = new AtomicInteger();
|
|
this.playerMap = new PlayerMap();
|
|
@@ -336,7 +336,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, this.level.kaiijuConfig.regionFormatLinearInternalCompressionLevel, 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);
|
|
@@ -950,13 +950,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;
|
|
@@ -974,7 +974,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 a5655ebb233f1e1e1dd7f79fdd948020478928fc..92430e71f64d88352838ee9cf0f4d22c050e7acc 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
@@ -390,9 +390,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, int linearInternalCompression, Path directory, boolean dsync) { // Kaiiju
|
|
+ super(format, linearCompression, linearInternalCompression, directory, dsync); // Kaiiju
|
|
}
|
|
|
|
protected void write(ChunkPos pos, net.minecraft.nbt.CompoundTag nbt) throws IOException {
|
|
@@ -583,7 +582,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, this.getLevel().kaiijuConfig.regionFormatLinearInternalCompressionLevel, 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 759b125cc1251b9b4f1f443c9f70c482ef5b32f8..761293b17cf12e28487034508906702bae2a3feb 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,14 @@ 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();
|
|
+ net.minecraft.server.level.ServerLevel level = ((org.bukkit.craftbukkit.CraftWorld) org.bukkit.Bukkit.getWorld(worldName)).getHandle();
|
|
+ String formatName = level.kaiijuConfig.regionFormatName;
|
|
+ int linearCompression = level.kaiijuConfig.regionFormatLinearCompressionLevel;
|
|
+ int linearInternalCompression = level.kaiijuConfig.regionFormatLinearInternalCompressionLevel;
|
|
+ builder1.put(resourcekey1, new ChunkStorage(formatName, linearCompression, linearInternalCompression, path.resolve("region"), this.dataFixer, true));
|
|
+ // Kaiiju end
|
|
}
|
|
|
|
ImmutableMap<ResourceKey<LevelStem>, ChunkStorage> immutablemap1 = builder1.build(); // CraftBukkit
|
|
@@ -235,7 +241,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 +260,13 @@ 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();
|
|
+ net.minecraft.server.level.ServerLevel level = ((org.bukkit.craftbukkit.CraftWorld) org.bukkit.Bukkit.getWorld(worldName)).getHandle();
|
|
+ int linearCompression = level.kaiijuConfig.regionFormatLinearCompressionLevel;
|
|
+ int linearInternalCompression = level.kaiijuConfig.regionFormatLinearInternalCompressionLevel;
|
|
+ dev.kaiijumc.kaiiju.region.AbstractRegionFile regionfile = dev.kaiijumc.kaiiju.region.AbstractRegionFileFactory.getAbstractRegionFile(linearCompression, linearInternalCompression, 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 8950b220b9a3512cd4667beb7bdec0e82e07edc6..ca93af37207a78e41b6d20333638bec32c11c416 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
|
|
@@ -57,8 +57,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, int linearInternalCompression, Path path, DataFixer dataFixer, boolean dsync, RegistryAccess registryManager, LevelHeightAccessor world) { // Kaiiju
|
|
+ super(formatName, linearCompression, linearInternalCompression, 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 5a425be023d77f0370d102dfb52427147849ac1a..527384a43f5c512273cce0d16a3bc887e24357c2 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, int linearInternalCompression, 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, linearInternalCompression, directory, dsync, true); // Paper - nuke IOWorker // Paper // Kaiiju
|
|
// 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 bd502ca721de0cab438d995efa00ad0554c0d2fe..ad784bc53d5c9a4abb944ce21e5bb81b939ecbea 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,17 +22,27 @@ 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;
|
|
+ public final int linearInternalCompression;
|
|
+ // Kaiiju end
|
|
|
|
private final boolean isChunkData; // Paper
|
|
|
|
- protected RegionFileStorage(Path directory, boolean dsync) { // Paper - protected constructor
|
|
+ protected RegionFileStorage(String format, int linearCompression, int linearInternalCompression, Path directory, boolean dsync) { // Paper - protected constructor
|
|
// Paper start - add isChunkData param
|
|
- this(directory, dsync, false);
|
|
+ this(format, linearCompression, linearInternalCompression, directory, dsync, false);
|
|
}
|
|
- RegionFileStorage(Path directory, boolean dsync, boolean isChunkData) {
|
|
+ RegionFileStorage(String format, int linearCompression, int linearInternalCompression, Path directory, boolean dsync, boolean isChunkData) { // Kaiiju
|
|
+ // Kaiiju start
|
|
+ this.format = format;
|
|
+ this.linearCompression = linearCompression;
|
|
+ this.linearInternalCompression = linearInternalCompression;
|
|
+ // Kaiiju end
|
|
this.isChunkData = isChunkData;
|
|
// Paper end - add isChunkData param
|
|
this.folder = directory;
|
|
@@ -42,7 +52,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;
|
|
}
|
|
|
|
@@ -62,49 +72,66 @@ 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());
|
|
- 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;
|
|
} else {
|
|
if (this.regionCache.size() >= io.papermc.paper.configuration.GlobalConfiguration.get().misc.regionFileCacheSize) { // Paper - configurable
|
|
- ((RegionFile) this.regionCache.removeLast()).close();
|
|
+ this.regionCache.removeLast().close(); // Kaiiju
|
|
}
|
|
|
|
FileUtil.createDirectoriesSafe(this.folder);
|
|
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)) return null; // CraftBukkit
|
|
- RegionFile regionfile1 = new RegionFile(path1, this.folder, this.sync, this.isChunkData); // Paper - allow for chunk regionfiles to regen header
|
|
+ // 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 return null;
|
|
+ } else {
|
|
+ String extension = switch (this.format) {
|
|
+ case "LINEAR" -> "linear";
|
|
+ default -> "mca";
|
|
+ };
|
|
+ path1 = path.resolve("r." + j + "." + chunkcoordintpair.getRegionZ() + "." + extension);
|
|
+ }
|
|
+ //if (existingOnly && !java.nio.file.Files.exists(path1)) return null; // CraftBukkit
|
|
+ // Kaiiju end
|
|
+
|
|
+ dev.kaiijumc.kaiiju.region.AbstractRegionFile regionfile1 = dev.kaiijumc.kaiiju.region.AbstractRegionFileFactory.getAbstractRegionFile(this.linearCompression, this.linearInternalCompression, 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;
|
|
@@ -132,7 +159,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);
|
|
@@ -179,14 +206,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
|
|
@@ -196,7 +223,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
|
|
@@ -210,12 +237,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;
|
|
}
|
|
}
|
|
@@ -249,13 +276,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;
|
|
}
|
|
@@ -285,7 +312,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;
|
|
}
|
|
@@ -335,7 +362,7 @@ public class RegionFileStorage implements AutoCloseable {
|
|
}
|
|
// Paper end
|
|
} finally { // Paper start
|
|
- regionfile.fileLock.unlock();
|
|
+ regionfile.getFileLock().unlock(); // Kaiiju
|
|
} // Paper end
|
|
}
|
|
|
|
@@ -344,7 +371,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();
|
|
@@ -360,7 +387,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 d783072bc964e45c308197e6f79874eb4a09f871..f335bb86031c4efbc88f44d542eefcc6815cd2b7 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, int linearInternalCompression, 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, linearInternalCompression, 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 6a327616cd590b70170f8441c003a2109640201d..2ef0a41fd9ee60bc0866cf5c678498c68250689c 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
@@ -552,7 +552,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) {
|