13
0
geforkt von Mirrors/Paper

async chunk patch progress (#6930)

Dieser Commit ist enthalten in:
Jake Potrebic 2021-11-23 12:34:20 -08:00
Ursprung 682bb3f9c0
Commit ddaf1cb4e2
6 geänderte Dateien mit 86 neuen und 117 gelöschten Zeilen

Datei anzeigen

@ -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;

Datei anzeigen

@ -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

Datei anzeigen

@ -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")