mirror of
https://github.com/PaperMC/Paper.git
synced 2024-11-23 11:06:29 +01:00
385f313a8b
Upstream has released updates that appear to apply and compile correctly. This update has not been tested by PaperMC and as with ANY update, please do your own testing Bukkit Changes: d41796de SPIGOT-7071: Add Player#stopSound(SoundCategory category) 61dae5b2 SPIGOT-7011, SPIGOT-7065: Overhaul of structures CraftBukkit Changes: 991aeda12 SPIGOT-1729, SPIGOT-7090: Keep precision in teleportation between worlds 5c9a5f628 SPIGOT-7071: Add Player#stopSound(SoundCategory category) 68f888ded SPIGOT-7011, SPIGOT-7065: Overhaul of structures 0231a3746 Remove outdated build delay. Spigot Changes: 475f6008 Rebuild patches 8ce1761f Rebuild patches
291 lines
14 KiB
Diff
291 lines
14 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
|
Date: Sat, 15 Jun 2019 08:54:33 -0700
|
|
Subject: [PATCH] Fix World#isChunkGenerated calls
|
|
|
|
Optimize World#loadChunk() too
|
|
This patch also adds a chunk status cache on region files (note that
|
|
its only purpose is to cache the status on DISK)
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
index b289d6c166625947fb2abb4159bbec651a1067f3..b6407f47e1be630f93ad185d56304f0ecbada6eb 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
@@ -1249,9 +1249,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
// Paper end
|
|
|
|
private CompletableFuture<Optional<CompoundTag>> readChunk(ChunkPos chunkPos) {
|
|
- return this.read(chunkPos).thenApplyAsync((optional) -> {
|
|
- return optional.map((nbttagcompound) -> this.upgradeChunkTag(nbttagcompound, chunkPos)); // CraftBukkit
|
|
- }, Util.backgroundExecutor());
|
|
+ // Paper start - Cache chunk status on disk
|
|
+ try {
|
|
+ return CompletableFuture.completedFuture(Optional.ofNullable(this.readConvertChunkSync(chunkPos)));
|
|
+ } catch (Throwable thr) {
|
|
+ return CompletableFuture.failedFuture(thr);
|
|
+ }
|
|
+ // Paper end - Cache chunk status on disk
|
|
}
|
|
|
|
// CraftBukkit start
|
|
@@ -1260,6 +1264,63 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
// CraftBukkit end
|
|
}
|
|
|
|
+ // Paper start - Cache chunk status on disk
|
|
+ @Nullable
|
|
+ public CompoundTag readConvertChunkSync(ChunkPos pos) throws IOException {
|
|
+ CompoundTag nbttagcompound = this.readSync(pos);
|
|
+ // Paper start - Cache chunk status on disk
|
|
+ if (nbttagcompound == null) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ nbttagcompound = this.upgradeChunkTag(nbttagcompound, pos); // CraftBukkit
|
|
+ if (nbttagcompound == null) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ this.updateChunkStatusOnDisk(pos, nbttagcompound);
|
|
+
|
|
+ return nbttagcompound;
|
|
+ // Paper end
|
|
+ }
|
|
+
|
|
+ // Paper start - chunk status cache "api"
|
|
+ public ChunkStatus getChunkStatusOnDiskIfCached(ChunkPos chunkPos) {
|
|
+ net.minecraft.world.level.chunk.storage.RegionFile regionFile = regionFileCache.getRegionFileIfLoaded(chunkPos);
|
|
+
|
|
+ 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);
|
|
+
|
|
+ if (regionFile == null || !regionFileCache.chunkExists(chunkPos)) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ ChunkStatus status = regionFile.getStatusIfCached(chunkPos.x, chunkPos.z);
|
|
+
|
|
+ if (status != null) {
|
|
+ return status;
|
|
+ }
|
|
+
|
|
+ this.readChunk(chunkPos);
|
|
+
|
|
+ return regionFile.getStatusIfCached(chunkPos.x, chunkPos.z);
|
|
+ }
|
|
+
|
|
+ public void updateChunkStatusOnDisk(ChunkPos chunkPos, @Nullable CompoundTag compound) throws IOException {
|
|
+ net.minecraft.world.level.chunk.storage.RegionFile regionFile = regionFileCache.getRegionFile(chunkPos, false);
|
|
+
|
|
+ regionFile.setStatus(chunkPos.x, chunkPos.z, ChunkSerializer.getStatus(compound));
|
|
+ }
|
|
+
|
|
+ public ChunkAccess getUnloadingChunk(int chunkX, int chunkZ) {
|
|
+ ChunkHolder chunkHolder = this.pendingUnloads.get(ChunkPos.asLong(chunkX, chunkZ));
|
|
+ return chunkHolder == null ? null : chunkHolder.getAvailableChunkNow();
|
|
+ }
|
|
+ // Paper end
|
|
+
|
|
boolean anyPlayerCloseEnoughForSpawning(ChunkPos pos) {
|
|
// Spigot start
|
|
return this.anyPlayerCloseEnoughForSpawning(pos, false);
|
|
diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkStatus.java b/src/main/java/net/minecraft/world/level/chunk/ChunkStatus.java
|
|
index 441d46635caedfae3cb2f46d30b8d9ae95636e7b..e6240f891e396d91e31b02fdf3084be77e9d6697 100644
|
|
--- a/src/main/java/net/minecraft/world/level/chunk/ChunkStatus.java
|
|
+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkStatus.java
|
|
@@ -292,6 +292,17 @@ public class ChunkStatus {
|
|
return this.chunkType;
|
|
}
|
|
|
|
+ // Paper start
|
|
+ public static ChunkStatus getStatus(String name) {
|
|
+ try {
|
|
+ // We need this otherwise we return EMPTY for invalid names
|
|
+ ResourceLocation key = new ResourceLocation(name);
|
|
+ return Registry.CHUNK_STATUS.getOptional(key).orElse(null);
|
|
+ } catch (Exception ex) {
|
|
+ return null; // invalid name
|
|
+ }
|
|
+ }
|
|
+ // Paper end
|
|
public static ChunkStatus byName(String id) {
|
|
return (ChunkStatus) Registry.CHUNK_STATUS.get(ResourceLocation.tryParse(id));
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
|
|
index 8cc2a2c026eb44461cd94faeb64fb2151d2d3898..8480310bd389ad55b8138a20da59cbcffb973819 100644
|
|
--- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
|
|
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
|
|
@@ -623,6 +623,17 @@ public class ChunkSerializer {
|
|
}));
|
|
}
|
|
|
|
+ // Paper start
|
|
+ public static @Nullable ChunkStatus getStatus(@Nullable CompoundTag compound) {
|
|
+ if (compound == null) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ // Note: Copied from below
|
|
+ return ChunkStatus.getStatus(compound.getString("Status"));
|
|
+ }
|
|
+ // Paper end
|
|
+
|
|
public static ChunkStatus.ChunkType getChunkTypeFromTag(@Nullable CompoundTag nbt) {
|
|
return nbt != null ? ChunkStatus.byName(nbt.getString("Status")).getChunkType() : ChunkStatus.ChunkType.PROTOCHUNK;
|
|
}
|
|
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 330fb8e6565a5c0490af0c5ca0e7355d81a82e58..861a25a15f1aab20e3245b6d5cdad5d23bdfd6d0 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
|
|
@@ -51,6 +51,30 @@ 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
|
|
|
|
+ // Paper start - Cache chunk status
|
|
+ private final net.minecraft.world.level.chunk.ChunkStatus[] statuses = new net.minecraft.world.level.chunk.ChunkStatus[32 * 32];
|
|
+
|
|
+ private boolean closed;
|
|
+
|
|
+ // invoked on write/read
|
|
+ public void setStatus(int x, int z, net.minecraft.world.level.chunk.ChunkStatus status) {
|
|
+ if (this.closed) {
|
|
+ // We've used an invalid region file.
|
|
+ throw new IllegalStateException("RegionFile is closed");
|
|
+ }
|
|
+ this.statuses[getChunkLocation(x, z)] = status;
|
|
+ }
|
|
+
|
|
+ public net.minecraft.world.level.chunk.ChunkStatus getStatusIfCached(int x, int z) {
|
|
+ if (this.closed) {
|
|
+ // We've used an invalid region file.
|
|
+ throw new IllegalStateException("RegionFile is closed");
|
|
+ }
|
|
+ final int location = getChunkLocation(x, z);
|
|
+ return this.statuses[location];
|
|
+ }
|
|
+ // Paper end
|
|
+
|
|
public RegionFile(Path file, Path directory, boolean dsync) throws IOException {
|
|
this(file, directory, RegionFileVersion.VERSION_DEFLATE, dsync);
|
|
}
|
|
@@ -398,6 +422,7 @@ public class RegionFile implements AutoCloseable {
|
|
return this.getOffset(pos) != 0;
|
|
}
|
|
|
|
+ private static int getChunkLocation(int x, int z) { return (x & 31) + (z & 31) * 32; } // Paper - OBFHELPER - sort of, mirror of logic below
|
|
private static int getOffsetIndex(ChunkPos pos) {
|
|
return pos.getRegionLocalX() + pos.getRegionLocalZ() * 32;
|
|
}
|
|
@@ -408,6 +433,7 @@ public class RegionFile implements AutoCloseable {
|
|
synchronized (this) {
|
|
try {
|
|
// Paper end
|
|
+ this.closed = true; // Paper
|
|
try {
|
|
this.padToFullSector();
|
|
} finally {
|
|
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 e734d82c2863caca8abc6bb8bb82882e971c36a8..a96a6af2bcec3134b7caa32299bd07af50e83b89 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
|
|
@@ -245,6 +245,7 @@ public class RegionFileStorage implements AutoCloseable {
|
|
|
|
try {
|
|
NbtIo.write(nbt, (DataOutput) dataoutputstream);
|
|
+ regionfile.setStatus(pos.x, pos.z, ChunkSerializer.getStatus(nbt)); // Paper - cache status on disk
|
|
regionfile.setOversized(pos.x, pos.z, false); // Paper - We don't do this anymore, mojang stores differently, but clear old meta flag if it exists to get rid of our own meta file once last oversized is gone
|
|
} catch (Throwable throwable) {
|
|
if (dataoutputstream != null) {
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
index 2518674a967a5fbf2229a0fdd60447a62cfbf856..cd70aa0fa7a575c3c1f6434db74c1cc8342fddb4 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
@@ -300,9 +300,23 @@ public class CraftWorld extends CraftRegionAccessor implements World {
|
|
|
|
@Override
|
|
public boolean isChunkGenerated(int x, int z) {
|
|
+ // Paper start - Fix this method
|
|
+ if (!Bukkit.isPrimaryThread()) {
|
|
+ return java.util.concurrent.CompletableFuture.supplyAsync(() -> {
|
|
+ return CraftWorld.this.isChunkGenerated(x, z);
|
|
+ }, world.getChunkSource().mainThreadProcessor).join();
|
|
+ }
|
|
+ ChunkAccess chunk = world.getChunkSource().getChunkAtImmediately(x, z);
|
|
+ if (chunk == null) {
|
|
+ chunk = world.getChunkSource().chunkMap.getUnloadingChunk(x, z);
|
|
+ }
|
|
+ if (chunk != null) {
|
|
+ return chunk instanceof ImposterProtoChunk || chunk instanceof net.minecraft.world.level.chunk.LevelChunk;
|
|
+ }
|
|
try {
|
|
- return this.isChunkLoaded(x, z) || this.world.getChunkSource().chunkMap.read(new ChunkPos(x, z)).get().isPresent();
|
|
- } catch (InterruptedException | ExecutionException ex) {
|
|
+ return world.getChunkSource().chunkMap.getChunkStatusOnDisk(new ChunkPos(x, z)) == ChunkStatus.FULL;
|
|
+ // Paper end
|
|
+ } catch (IOException ex) {
|
|
throw new RuntimeException(ex);
|
|
}
|
|
}
|
|
@@ -413,20 +427,48 @@ public class CraftWorld extends CraftRegionAccessor implements World {
|
|
@Override
|
|
public boolean loadChunk(int x, int z, boolean generate) {
|
|
org.spigotmc.AsyncCatcher.catchOp("chunk load"); // Spigot
|
|
- ChunkAccess chunk = this.world.getChunkSource().getChunk(x, z, generate || isChunkGenerated(x, z) ? ChunkStatus.FULL : ChunkStatus.EMPTY, true); // Paper
|
|
+ // Paper start - Optimize this method
|
|
+ ChunkPos chunkPos = new ChunkPos(x, z);
|
|
|
|
- // If generate = false, but the chunk already exists, we will get this back.
|
|
- if (chunk instanceof ImposterProtoChunk) {
|
|
- // We then cycle through again to get the full chunk immediately, rather than after the ticket addition
|
|
- chunk = this.world.getChunkSource().getChunk(x, z, ChunkStatus.FULL, true);
|
|
- }
|
|
+ if (!generate) {
|
|
+ ChunkAccess immediate = world.getChunkSource().getChunkAtImmediately(x, z);
|
|
+ if (immediate == null) {
|
|
+ immediate = world.getChunkSource().chunkMap.getUnloadingChunk(x, z);
|
|
+ }
|
|
+ if (immediate != null) {
|
|
+ if (!(immediate instanceof ImposterProtoChunk) && !(immediate instanceof net.minecraft.world.level.chunk.LevelChunk)) {
|
|
+ return false; // not full status
|
|
+ }
|
|
+ world.getChunkSource().addRegionTicket(TicketType.PLUGIN, chunkPos, 1, Unit.INSTANCE);
|
|
+ world.getChunk(x, z); // make sure we're at ticket level 32 or lower
|
|
+ return true;
|
|
+ }
|
|
|
|
- if (chunk instanceof net.minecraft.world.level.chunk.LevelChunk) {
|
|
- this.world.getChunkSource().addRegionTicket(TicketType.PLUGIN, new ChunkPos(x, z), 1, Unit.INSTANCE);
|
|
- return true;
|
|
+ net.minecraft.world.level.chunk.storage.RegionFile file;
|
|
+ try {
|
|
+ file = world.getChunkSource().chunkMap.regionFileCache.getRegionFile(chunkPos, false);
|
|
+ } catch (IOException ex) {
|
|
+ throw new RuntimeException(ex);
|
|
+ }
|
|
+
|
|
+ ChunkStatus status = file.getStatusIfCached(x, z);
|
|
+ if (!file.hasChunk(chunkPos) || (status != null && status != ChunkStatus.FULL)) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ ChunkAccess chunk = world.getChunkSource().getChunk(x, z, ChunkStatus.EMPTY, true);
|
|
+ if (!(chunk instanceof ImposterProtoChunk) && !(chunk instanceof net.minecraft.world.level.chunk.LevelChunk)) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ // fall through to load
|
|
+ // we do this so we do not re-read the chunk data on disk
|
|
}
|
|
|
|
- return false;
|
|
+ world.getChunkSource().addRegionTicket(TicketType.PLUGIN, chunkPos, 1, Unit.INSTANCE);
|
|
+ world.getChunkSource().getChunk(x, z, ChunkStatus.FULL, true);
|
|
+ return true;
|
|
+ // Paper end
|
|
}
|
|
|
|
@Override
|