diff --git a/build-data/paper.at b/build-data/paper.at index 2790d40943..777d1ed2a2 100644 --- a/build-data/paper.at +++ b/build-data/paper.at @@ -50,7 +50,7 @@ public net.minecraft.world.entity.player.Player removeEntitiesOnShoulder()V # LivingEntity setkiller public net.minecraft.world.entity.LivingEntity lastHurtByPlayerTime -# SkeletonHorse Addittions +# SkeletonHorse Additions public net.minecraft.world.entity.animal.horse.SkeletonHorse trapTime # Fix client rendering skulls @@ -66,6 +66,7 @@ public-f net.minecraft.world.level.chunk.storage.RegionFileStorage public net.minecraft.world.level.chunk.storage.RegionFileStorage getFile(Lnet/minecraft/world/level/ChunkPos;Z)Lnet/minecraft/world/level/chunk/storage/RegionFile; public net.minecraft.world.level.chunk.storage.SectionStorage dirty public net.minecraft.util.thread.BlockableEventLoop runAllTasks()V +public net.minecraft.server.level.ChunkMap getPoiManager()Lnet/minecraft/world/entity/ai/village/poi/PoiManager; # Improve death events public net.minecraft.world.entity.LivingEntity getDeathSound()Lnet/minecraft/sounds/SoundEvent; diff --git a/patches/unapplied/api/Add-source-block-constructor-and-getChangedBlockData.patch b/patches/api/Add-source-block-constructor-and-getChangedBlockData.patch similarity index 100% rename from patches/unapplied/api/Add-source-block-constructor-and-getChangedBlockData.patch rename to patches/api/Add-source-block-constructor-and-getChangedBlockData.patch diff --git a/patches/unapplied/api/Async-Chunks-API.patch b/patches/api/Async-Chunks-API.patch similarity index 100% rename from patches/unapplied/api/Async-Chunks-API.patch rename to patches/api/Async-Chunks-API.patch diff --git a/patches/unapplied/api/isChunkGenerated-API.patch b/patches/api/isChunkGenerated-API.patch similarity index 100% rename from patches/unapplied/api/isChunkGenerated-API.patch rename to patches/api/isChunkGenerated-API.patch diff --git a/patches/server/Asynchronous-chunk-IO-and-loading.patch b/patches/server/Asynchronous-chunk-IO-and-loading.patch index f2e23de52b..cd34d35ab9 100644 --- a/patches/server/Asynchronous-chunk-IO-and-loading.patch +++ b/patches/server/Asynchronous-chunk-IO-and-loading.patch @@ -3,8 +3,7 @@ From: Spottedleaf <Spottedleaf@users.noreply.github.com> Date: Sat, 13 Jul 2019 09:23:10 -0700 Subject: [PATCH] Asynchronous chunk IO and loading -# UPDATE NOTES: RegionFileStorage and SectionStorage need resolving (will conflict on apply) -# ChunkSerializer needs the new tick lists to be saved (see added todos) +ChunkSerializer needs the new tick lists to be saved (see added todos) This patch re-adds a file IO thread as well as shoving de-serializing chunk NBT data onto worker threads. This patch also will shove @@ -1540,8 +1539,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + // apply fixes + + try { -+ chunkData.chunkData = chunkManager.getChunkData(this.world.getTypeKey(), -+ chunkManager.overworldDataStorage, chunkData.chunkData, chunkPos, this.world); // clone data for safety, file IO thread does not clone ++ chunkData.chunkData = chunkManager.upgradeChunkTag(this.world.getTypeKey(), ++ chunkManager.overworldDataStorage, chunkData.chunkData, chunkManager.generator.getTypeNameForDataFixer(), chunkPos, this.world); // clone data for safety, file IO thread does not clone + } catch (final Throwable ex) { + PaperFileIOThread.LOGGER.error("Could not apply datafixers for chunk task: " + this.toString(), ex); + this.complete(ChunkLoadTask.createEmptyHolder()); @@ -1552,8 +1551,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + } + + try { -+ chunkHolder = ChunkSerializer.loadChunk(this.world, -+ chunkManager.structureManager, chunkManager.getVillagePlace(), chunkPos, ++ chunkHolder = ChunkSerializer.loadChunk(this.world, chunkManager.getPoiManager(), chunkPos, + chunkData.chunkData, true); + } catch (final Throwable ex) { + PaperFileIOThread.LOGGER.error("Could not de-serialize chunk data for task: " + this.toString(), ex); @@ -2280,19 +2278,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 } @Override -diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/server/MCUtil.java -+++ b/src/main/java/net/minecraft/server/MCUtil.java -@@ -0,0 +0,0 @@ public final class MCUtil { - public static int getTicketLevelFor(net.minecraft.world.level.chunk.ChunkStatus status) { - return net.minecraft.server.level.ChunkMap.MAX_VIEW_DISTANCE + net.minecraft.world.level.chunk.ChunkStatus.getDistance(status); - } -+ -+ public static int getTicketLevelFor(net.minecraft.world.level.chunk.ChunkStatus status) { -+ return net.minecraft.server.level.ChunkMap.MAX_VIEW_DISTANCE + net.minecraft.world.level.chunk.ChunkStatus.getDistance(status); -+ } - } diff --git a/src/main/java/net/minecraft/server/Main.java b/src/main/java/net/minecraft/server/Main.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/server/Main.java @@ -2322,25 +2307,6 @@ diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/mai index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/server/level/ChunkHolder.java +++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java -@@ -0,0 +0,0 @@ public class ChunkHolder { - return chunkstatus; - } - } -+ return null; -+ } -+ -+ public ChunkStatus getChunkHolderStatus() { -+ for (ChunkStatus curr = ChunkStatus.FULL, next = curr.getParent(); curr != next; curr = next, next = next.getParent()) { -+ CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> future = this.getFutureIfPresentUnchecked(curr); -+ Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure> either = future.getNow(null); -+ if (either == null || !either.left().isPresent()) { -+ continue; -+ } -+ return curr; -+ } - - return null; - } @@ -0,0 +0,0 @@ public class ChunkHolder { ChunkStatus chunkstatus = ChunkHolder.getStatus(this.oldTicketLevel); ChunkStatus chunkstatus1 = ChunkHolder.getStatus(this.ticketLevel); @@ -2471,7 +2437,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + LOGGER.fatal("Failed to prepare async save, attempting synchronous save", ex); + this.save(ichunkaccess); + } -+ // Paper end - async chunk savin ++ // Paper end - async chunk saving if (this.entitiesInLevel.remove(pos) && ichunkaccess instanceof LevelChunk) { LevelChunk chunk = (LevelChunk) ichunkaccess; @@ -2504,6 +2470,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + if (chunkHolder.protoChunk != null) {try (Timing ignored2 = this.level.timings.chunkLoadLevelTimer.startTimingIfSync()) { // Paper start - timings // Paper - chunk is created async + if (true) { ++ ProtoChunk protochunk = chunkHolder.protoChunk; this.markPosition(pos, protochunk.getStatus().getChunkType()); return Either.left(protochunk); } @@ -2629,15 +2596,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 } // Paper end + // Paper start - async chunk io -+ public ChunkAccess getChunkAtImmediately(int x, int z) { -+ ChunkHolder holder = this.chunkMap.getVisibleChunkIfPresent(ChunkPos.asLong(x, z)); -+ if (holder == null) { -+ return null; -+ } -+ -+ return holder.getLastAvailable(); -+ } -+ + private long asyncLoadSeqCounter; + + public CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> getChunkAtAsynchronously(int x, int z, boolean gen, boolean isUrgent) { @@ -2736,14 +2694,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + return CompletableFuture.completedFuture(either); + }, this.mainThreadProcessor); + } -+ -+ public <T> void addTicketAtLevel(TicketType<T> ticketType, ChunkPos chunkPos, int ticketLevel, T identifier) { -+ this.distanceManager.addTicketAtLevel(ticketType, chunkPos, ticketLevel, identifier); -+ } -+ -+ public <T> void removeTicketAtLevel(TicketType<T> ticketType, ChunkPos chunkPos, int ticketLevel, T identifier) { -+ this.distanceManager.removeTicketAtLevel(ticketType, chunkPos, ticketLevel, identifier); -+ } + // Paper end - async chunk io @Nullable @@ -2812,21 +2762,21 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + public final com.destroystokyo.paper.io.PaperFileIOThread.ChunkDataController poiDataController = new com.destroystokyo.paper.io.PaperFileIOThread.ChunkDataController() { + @Override + public void writeData(int x, int z, net.minecraft.nbt.CompoundTag compound) throws java.io.IOException { -+ ServerLevel.this.getChunkSource().chunkMap.getVillagePlace().write(new ChunkPos(x, z), compound); ++ ServerLevel.this.getChunkSource().chunkMap.getPoiManager().write(new ChunkPos(x, z), compound); + } + + @Override + public net.minecraft.nbt.CompoundTag readData(int x, int z) throws java.io.IOException { -+ return ServerLevel.this.getChunkSource().chunkMap.getVillagePlace().read(new ChunkPos(x, z)); ++ return ServerLevel.this.getChunkSource().chunkMap.getPoiManager().read(new ChunkPos(x, z)); + } + + @Override + public <T> T computeForRegionFile(int chunkX, int chunkZ, java.util.function.Function<net.minecraft.world.level.chunk.storage.RegionFile, T> function) { -+ synchronized (ServerLevel.this.getChunkSource().chunkMap.getVillagePlace()) { ++ synchronized (ServerLevel.this.getChunkSource().chunkMap.getPoiManager()) { + net.minecraft.world.level.chunk.storage.RegionFile file; + + try { -+ file = ServerLevel.this.getChunkSource().chunkMap.getVillagePlace().getFile(new ChunkPos(chunkX, chunkZ), false); ++ file = ServerLevel.this.getChunkSource().chunkMap.getPoiManager().getRegionFile(new ChunkPos(chunkX, chunkZ), false); + } catch (java.io.IOException ex) { + throw new RuntimeException(ex); + } @@ -2837,8 +2787,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + + @Override + public <T> T computeForRegionFileIfLoaded(int chunkX, int chunkZ, java.util.function.Function<net.minecraft.world.level.chunk.storage.RegionFile, T> function) { -+ synchronized (ServerLevel.this.getChunkSource().chunkMap.getVillagePlace()) { -+ net.minecraft.world.level.chunk.storage.RegionFile file = ServerLevel.this.getChunkSource().chunkMap.getVillagePlace().getRegionFileIfLoaded(new ChunkPos(chunkX, chunkZ)); ++ synchronized (ServerLevel.this.getChunkSource().chunkMap.getPoiManager()) { ++ net.minecraft.world.level.chunk.storage.RegionFile file = ServerLevel.this.getChunkSource().chunkMap.getPoiManager().getRegionFileIfLoaded(new ChunkPos(chunkX, chunkZ)); + return function.apply(file); + } + } @@ -2861,7 +2811,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + net.minecraft.world.level.chunk.storage.RegionFile file; + + try { -+ file = ServerLevel.this.getChunkSource().chunkMap.regionFileCache.getFile(new ChunkPos(chunkX, chunkZ), false); ++ file = ServerLevel.this.getChunkSource().chunkMap.regionFileCache.getRegionFile(new ChunkPos(chunkX, chunkZ), false); + } catch (java.io.IOException ex) { + throw new RuntimeException(ex); + } @@ -3085,9 +3035,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 - return protochunk1; + return new InProgressChunkHolder(protochunk1, tasksToExecuteOnMain); // Paper - Async chunk loading - } - } - ++ } ++ } ++ + // Paper start - async chunk save for unload + public static final class AsyncSaveData { + public final DataLayer[] blockLight; @@ -3107,9 +3057,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + this.fluidTickList = fluidTickList; + this.blockEntities = blockEntities; + this.worldTime = worldTime; -+ } -+ } -+ + } + } + + // must be called sync + public static AsyncSaveData getAsyncSaveData(ServerLevel world, ChunkAccess chunk) { + org.spigotmc.AsyncCatcher.catchOp("preparation of chunk data for async save"); @@ -3139,22 +3089,22 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + net.minecraft.world.ticks.TickContainerAccess<Block> blockTickList = chunk.getBlockTicks(); + + //TODO check ChunkSerializer "block_ticks" -+ ListTag blockTickListSerialized; -+ if (blockTickList instanceof ProtoTickList || blockTickList instanceof ChunkTickList) { -+ blockTickListSerialized = null; -+ } else { -+ blockTickListSerialized = world.getBlockTicks().save(chunkPos); -+ } ++ ListTag blockTickListSerialized = null; // Paper - remove null ++ // if (blockTickList instanceof ProtoTickList || blockTickList instanceof ChunkTickList) { ++ // blockTickListSerialized = null; ++ // } else { ++ // blockTickListSerialized = world.getBlockTicks().save(chunkPos); ++ // } + + net.minecraft.world.ticks.TickContainerAccess<Fluid> fluidTickList = chunk.getFluidTicks(); + + //TODO -+ ListTag fluidTickListSerialized; -+ if (fluidTickList instanceof ProtoTickList || fluidTickList instanceof ChunkTickList) { -+ fluidTickListSerialized = null; -+ } else { -+ fluidTickListSerialized = world.getFluidTicks().save(chunkPos); -+ } ++ ListTag fluidTickListSerialized = null; // Paper - remove null ++ // if (fluidTickList instanceof ProtoTickList || fluidTickList instanceof ChunkTickList) { ++ // fluidTickListSerialized = null; ++ // } else { ++ // fluidTickListSerialized = world.getFluidTicks().save(chunkPos); ++ // } + + ListTag blockEntitiesSerialized = new ListTag(); + for (final BlockPos blockPos : chunk.getBlockEntitiesPos()) { @@ -3182,9 +3132,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 CompoundTag nbttagcompound = new CompoundTag(); @@ -0,0 +0,0 @@ public class ChunkSerializer { + nbttagcompound.putInt("xPos", chunkcoordintpair.x); nbttagcompound.putInt("yPos", chunk.getMinSection()); nbttagcompound.putInt("zPos", chunkcoordintpair.z); - nbttagcompound.putLong("LastUpdate", world.getGameTime()); +- nbttagcompound.putLong("LastUpdate", world.getGameTime()); + nbttagcompound.putLong("LastUpdate", asyncsavedata != null ? asyncsavedata.worldTime : world.getGameTime()); // Paper - async chunk unloading nbttagcompound.putLong("InhabitedTime", chunk.getInhabitedTime()); nbttagcompound.putString("Status", chunk.getStatus().getName()); @@ -3333,6 +3284,19 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 } public ChunkScanAccess chunkScanner() { +- return this.worker; ++ // Paper start - nuke IO worker ++ return ((chunkPos, streamTagVisitor) -> { ++ try { ++ this.regionFileCache.scanChunk(chunkPos, streamTagVisitor); ++ return java.util.concurrent.CompletableFuture.completedFuture(null); ++ } catch (IOException e) { ++ throw new RuntimeException(e); ++ } ++ }); ++ // Paper end + } + } diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java @@ -3384,27 +3348,27 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 @@ -0,0 +0,0 @@ public class RegionFileStorage implements AutoCloseable { this.sync = dsync; } - -- public RegionFile getFile(ChunkPos chunkcoordintpair, boolean existingOnly) throws IOException { // CraftBukkit + +- private RegionFile getRegionFile(ChunkPos chunkcoordintpair, boolean existingOnly) throws IOException { // CraftBukkit + // Paper start + public synchronized RegionFile getRegionFileIfLoaded(ChunkPos chunkcoordintpair) { + return this.regionCache.getAndMoveToFirst(ChunkPos.asLong(chunkcoordintpair.getRegionX(), chunkcoordintpair.getRegionZ())); + } + + public synchronized boolean chunkExists(ChunkPos pos) throws IOException { -+ RegionFile regionfile = getFile(pos, true); ++ RegionFile regionfile = getRegionFile(pos, true); + + return regionfile != null ? regionfile.hasChunk(pos) : false; + } + -+ public synchronized RegionFile getFile(ChunkPos chunkcoordintpair, boolean existingOnly) throws IOException { // CraftBukkit -+ return this.getFile(chunkcoordintpair, existingOnly, false); ++ public synchronized RegionFile getRegionFile(ChunkPos chunkcoordintpair, boolean existingOnly) throws IOException { // CraftBukkit ++ return this.getRegionFile(chunkcoordintpair, existingOnly, false); + } -+ public synchronized RegionFile getFile(ChunkPos chunkcoordintpair, boolean existingOnly, boolean lock) throws IOException { ++ public synchronized RegionFile getRegionFile(ChunkPos chunkcoordintpair, boolean existingOnly, boolean lock) throws IOException { + // Paper end long i = ChunkPos.asLong(chunkcoordintpair.getRegionX(), chunkcoordintpair.getRegionZ()); RegionFile regionfile = (RegionFile) this.regionCache.getAndMoveToFirst(i); - + if (regionfile != null) { + // Paper start + if (lock) { @@ -3416,8 +3380,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 } else { if (this.regionCache.size() >= com.destroystokyo.paper.PaperConfig.regionFileCacheSize) { // Paper - configurable @@ -0,0 +0,0 @@ public class RegionFileStorage implements AutoCloseable { - RegionFile regionfile1 = new RegionFile(file1, this.folder, this.sync); - + RegionFile regionfile1 = new RegionFile(path1, this.folder, this.sync); + this.regionCache.putAndMoveToFirst(i, regionfile1); + // Paper start + if (lock) { @@ -3432,54 +3396,58 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 @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.getFile(pos, true); -+ RegionFile regionfile = this.getFile(pos, true, true); // Paper +- RegionFile regionfile = this.getRegionFile(pos, true); ++ RegionFile regionfile = this.getRegionFile(pos, true, true); // Paper if (regionfile == null) { return null; } // CraftBukkit end + try { // Paper DataInputStream datainputstream = regionfile.getChunkDataInputStream(pos); - + CompoundTag nbttagcompound; @@ -0,0 +0,0 @@ public class RegionFileStorage implements AutoCloseable { } - + return nbttagcompound; + } finally { // Paper start + regionfile.fileLock.unlock(); + } // Paper end } - + + public void scanChunk(ChunkPos chunkcoordintpair, StreamTagVisitor streamtagvisitor) throws IOException { +@@ -0,0 +0,0 @@ public class RegionFileStorage implements AutoCloseable { + } + protected void write(ChunkPos pos, @Nullable CompoundTag nbt) throws IOException { -- RegionFile regionfile = this.getFile(pos, false); // CraftBukkit -+ RegionFile regionfile = this.getFile(pos, false, true); // CraftBukkit // Paper +- RegionFile regionfile = this.getRegionFile(pos, false); // CraftBukkit ++ RegionFile regionfile = this.getRegionFile(pos, false, true); // CraftBukkit // Paper + try { // Paper int attempts = 0; Exception laste = null; while (attempts++ < 5) { try { // Paper - + if (nbt == null) { @@ -0,0 +0,0 @@ public class RegionFileStorage implements AutoCloseable { - MinecraftServer.LOGGER.error("Failed to save chunk", laste); + net.minecraft.server.MinecraftServer.LOGGER.error("Failed to save chunk", laste); } // Paper end + } finally { // Paper start + regionfile.fileLock.unlock(); + } // Paper end } - + - public void close() throws IOException { + public synchronized void close() throws IOException { // Paper -> synchronized ExceptionCollector<IOException> exceptionsuppressor = new ExceptionCollector<>(); ObjectIterator objectiterator = this.regionCache.values().iterator(); - + @@ -0,0 +0,0 @@ public class RegionFileStorage implements AutoCloseable { exceptionsuppressor.throwIfPresent(); } - + - public void flush() throws IOException { + public synchronized void flush() throws IOException { // Paper - synchronize ObjectIterator objectiterator = this.regionCache.values().iterator(); - + while (objectiterator.hasNext()) { 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 @@ -3488,7 +3456,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 @@ -0,0 +0,0 @@ import net.minecraft.world.level.LevelHeightAccessor; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; - + -public class SectionStorage<R> implements AutoCloseable { +public class SectionStorage<R> extends RegionFileStorage implements AutoCloseable { // Paper - nuke IOWorker private static final Logger LOGGER = LogManager.getLogger(); @@ -3500,22 +3468,22 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 private final Function<Runnable, Codec<R>> codec; @@ -0,0 +0,0 @@ public class SectionStorage<R> implements AutoCloseable { protected final LevelHeightAccessor levelHeightAccessor; - - public SectionStorage(File directory, Function<Runnable, Codec<R>> codecFactory, Function<Runnable, R> factory, DataFixer dataFixer, DataFixTypes dataFixTypes, boolean dsync, LevelHeightAccessor world) { -+ super(directory, dsync); // Paper - nuke IOWorker + + public SectionStorage(Path path, Function<Runnable, Codec<R>> codecFactory, Function<Runnable, R> factory, DataFixer dataFixer, DataFixTypes dataFixTypes, boolean dsync, LevelHeightAccessor world) { ++ super(path, dsync); this.codec = codecFactory; this.factory = factory; this.fixerUpper = dataFixer; this.type = dataFixTypes; this.levelHeightAccessor = world; -- this.worker = new IOWorker(directory, dsync, directory.getName()); +- this.worker = new IOWorker(path, dsync, path.getFileName().toString()); + // Paper - remove mojang I/O thread } - + protected void tick(BooleanSupplier shouldKeepTicking) { @@ -0,0 +0,0 @@ public class SectionStorage<R> implements AutoCloseable { } - + private void readColumn(ChunkPos chunkPos) { - this.readColumn(chunkPos, NbtOps.INSTANCE, this.tryRead(chunkPos)); + // Paper start - expose function to load in data @@ -3525,7 +3493,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + this.readColumn(chunkPos, NbtOps.INSTANCE, compound); + // Paper end - expose function to load in data } - + @Nullable private CompoundTag tryRead(ChunkPos pos) { try { @@ -3543,9 +3511,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 } else { LOGGER.error("Expected compound tag, got {}", (Object)tag); } - + } - + + // Paper start - internal get data function, copied from above + private CompoundTag getDataInternal(ChunkPos chunkcoordintpair) { + Dynamic<Tag> dynamic = this.writeColumn(chunkcoordintpair, NbtOps.INSTANCE); @@ -3561,7 +3529,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + // Paper end private <T> Dynamic<T> writeColumn(ChunkPos chunkPos, DynamicOps<T> dynamicOps) { Map<T, T> map = Maps.newHashMap(); - + @@ -0,0 +0,0 @@ public class SectionStorage<R> implements AutoCloseable { @Override diff --git a/settings.gradle.kts b/settings.gradle.kts index 1514614884..9afad2ea0c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -10,7 +10,7 @@ rootProject.name = "Paper" include( "Paper-API", "Paper-Server", - // "Paper-MojangAPI", // todo + "Paper-MojangAPI", ) val testPlugin = file("test-plugin.settings.gradle.kts")