mirror of
https://github.com/PaperMC/Paper.git
synced 2024-09-12 06:58:11 +02:00
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: 01aa02eb PR-858: Add LivingEntity#playHurtAnimation() 9421320f PR-884: Refinements to new ban API for improved compatibility and correctness 37a60b45 SPIGOT-6455, SPIGOT-7030, PR-750: Improve ban API 4eeb174b All smithing inventories are now the new smithing inventory f2bb168e PR-880: Add methods to get/set FallingBlock CancelDrop e7a807fa PR-879: Add Player#sendHealthUpdate() 692b8e96 SPIGOT-7370: Remove float value conversion in plugin.yml 2d033390 SPIGOT-7403: Add direct API for waxed signs 16a08373 PR-876: Add missing Raider API and 'no action ticks' CraftBukkit Changes: b60a95c8c PR-1189: Add LivingEntity#playHurtAnimation() 95c335c63 PR-1226: Fix VehicleEnterEvent not being called for certain entities 0a0fc3bee PR-1227: Refinements to new ban API for improved compatibility and correctness 0d0b1e5dc Revert bad change to PathfinderGoalSit causing all cats to sit 648196070 SPIGOT-6455, SPIGOT-7030, PR-1054: Improve ban API 31fe848d6 All smithing inventories are now the new smithing inventory 9a919a143 SPIGOT-7416: SmithItemEvent not firing in Smithing Table 9f64f0d22 PR-1221: Add methods to get/set FallingBlock CancelDrop 3be9ac171 PR-1220: Add Player#sendHealthUpdate() c1279f775 PR-1209: Clean up various patches c432e4397 Fix Raider#setCelebrating() implementation 504d96665 SPIGOT-7403: Add direct API for waxed signs c68c1f1b3 PR-1216: Add missing Raider API and 'no action ticks' 85b89c3dd Increase outdated build delay Spigot Changes: 9ebce8af Rebuild patches 64b565e6 Rebuild patches
247 lines
12 KiB
Diff
247 lines
12 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 af92411006c3d281815b3f4c3de5f0280d3a5901..50a201c08f143117a050305b0dde6873a04efb8b 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
@@ -687,9 +687,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
|
|
@@ -698,6 +702,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 = io.papermc.paper.chunk.system.ChunkSystem.getUnloadingChunkHolder(this.level, 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/storage/RegionFile.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
|
|
index 584985272a02eb5b61a22cf2404fbd97a55a3358..cda87a66fe80bf910f629c64e36c1fecbad81d77 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 96f129cb13642dc9667464b58c025fa0ed700cfd..29da08c58200c24fd03003937d30eb41234cabc9 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
|
|
@@ -290,6 +290,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 e0f38ef295e1958b45fc05395cd4c57194928338..f259f6609cd87a210451ddf4ea00a72718d1efd0 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
@@ -307,9 +307,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 (java.io.IOException ex) {
|
|
throw new RuntimeException(ex);
|
|
}
|
|
}
|
|
@@ -423,20 +437,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 (java.io.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
|