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 computeForRegionFile(final int chunkX, final int chunkZ, final Function function); - public abstract T computeForRegionFileIfLoaded(final int chunkX, final int chunkZ, final Function function); + public abstract T computeForRegionFile(final int chunkX, final int chunkZ, final Function function); // Kaiiju + public abstract T computeForRegionFileIfLoaded(final int chunkX, final int chunkZ, final Function 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 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 computeForRegionFile(final int chunkX, final int chunkZ, final boolean existingOnly, final Function function) { + public T computeForRegionFile(final int chunkX, final int chunkZ, final boolean existingOnly, final Function 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 computeForRegionFileIfLoaded(final int chunkX, final int chunkZ, final Function function) { + public T computeForRegionFileIfLoaded(final int chunkX, final int chunkZ, final Function 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 mainThreadExecutor, LightChunkGetter chunkProvider, ChunkGenerator chunkGenerator, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkStatusChangeListener, Supplier 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 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 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> 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 dimensionOptionsRegistry, boolean eraseCache) { @@ -115,8 +115,14 @@ public class WorldUpgrader { while (iterator1.hasNext()) { ResourceKey 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, 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 { // 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 regionCache = new Long2ObjectLinkedOpenHashMap(); + public final Long2ObjectLinkedOpenHashMap 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 extends RegionFileStorage implements AutoCloseabl public final RegistryAccess registryAccess; // Paper - rewrite chunk system protected final LevelHeightAccessor levelHeightAccessor; - public SectionStorage(Path path, Function> codecFactory, Function 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> codecFactory, Function 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) {