mirror of
https://github.com/PaperMC/Paper.git
synced 2024-08-20 03:13:29 +02:00
We still keep vanilla process of waiting for existing session to be removed before logging in by storing a separate map of pending. also fire the callback using executor incase further recursion causes any trouble
381 lines
17 KiB
Diff
381 lines
17 KiB
Diff
From fcceb8fb63706e669ee329b3cd5dc780332176f9 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/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java
|
|
index 0dd873a5dd..5e4f3612ba 100644
|
|
--- a/src/main/java/net/minecraft/server/ChunkProviderServer.java
|
|
+++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java
|
|
@@ -28,7 +28,7 @@ public class ChunkProviderServer extends IChunkProvider {
|
|
private final WorldServer world;
|
|
public final Thread serverThread; // Paper - private -> public
|
|
private final LightEngineThreaded lightEngine;
|
|
- private final ChunkProviderServer.a serverThreadQueue;
|
|
+ public final ChunkProviderServer.a serverThreadQueue; // Paper private -> public
|
|
public final PlayerChunkMap playerChunkMap;
|
|
private final WorldPersistentData worldPersistentData;
|
|
private long lastTickTime;
|
|
@@ -284,6 +284,21 @@ public class ChunkProviderServer extends IChunkProvider {
|
|
|
|
return ret;
|
|
}
|
|
+
|
|
+ @Nullable
|
|
+ public IChunkAccess getChunkAtImmediately(int x, int z) {
|
|
+ long k = ChunkCoordIntPair.pair(x, z);
|
|
+
|
|
+ // Note: Bypass cache to make this MT-Safe
|
|
+
|
|
+ PlayerChunk playerChunk = this.getChunk(k);
|
|
+ if (playerChunk == null) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ return playerChunk.getAvailableChunkNow();
|
|
+
|
|
+ }
|
|
// Paper end
|
|
|
|
@Nullable
|
|
diff --git a/src/main/java/net/minecraft/server/ChunkRegionLoader.java b/src/main/java/net/minecraft/server/ChunkRegionLoader.java
|
|
index 83bcef7e64..47e074a122 100644
|
|
--- a/src/main/java/net/minecraft/server/ChunkRegionLoader.java
|
|
+++ b/src/main/java/net/minecraft/server/ChunkRegionLoader.java
|
|
@@ -403,6 +403,17 @@ public class ChunkRegionLoader {
|
|
}
|
|
// Paper end
|
|
|
|
+ // Paper start
|
|
+ public static ChunkStatus getStatus(NBTTagCompound compound) {
|
|
+ if (compound == null) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ // Note: Copied from below
|
|
+ return ChunkStatus.getStatus(compound.getCompound("Level").getString("Status"));
|
|
+ }
|
|
+ // Paper end
|
|
+
|
|
public static ChunkStatus.Type a(@Nullable NBTTagCompound nbttagcompound) {
|
|
if (nbttagcompound != null) {
|
|
ChunkStatus chunkstatus = ChunkStatus.a(nbttagcompound.getCompound("Level").getString("Status"));
|
|
diff --git a/src/main/java/net/minecraft/server/ChunkStatus.java b/src/main/java/net/minecraft/server/ChunkStatus.java
|
|
index efdf611e66..134a4f0b7d 100644
|
|
--- a/src/main/java/net/minecraft/server/ChunkStatus.java
|
|
+++ b/src/main/java/net/minecraft/server/ChunkStatus.java
|
|
@@ -176,6 +176,7 @@ public class ChunkStatus {
|
|
return this.s;
|
|
}
|
|
|
|
+ public ChunkStatus getPreviousStatus() { return this.e(); } // Paper - OBFHELPER
|
|
public ChunkStatus e() {
|
|
return this.u;
|
|
}
|
|
@@ -196,6 +197,17 @@ public class ChunkStatus {
|
|
return this.y;
|
|
}
|
|
|
|
+ // Paper start
|
|
+ public static ChunkStatus getStatus(String name) {
|
|
+ try {
|
|
+ // We need this otherwise we return EMPTY for invalid names
|
|
+ MinecraftKey key = new MinecraftKey(name);
|
|
+ return IRegistry.CHUNK_STATUS.getOptional(key).orElse(null);
|
|
+ } catch (Exception ex) {
|
|
+ return null; // invalid name
|
|
+ }
|
|
+ }
|
|
+ // Paper end
|
|
public static ChunkStatus a(String s) {
|
|
return (ChunkStatus) IRegistry.CHUNK_STATUS.get(MinecraftKey.a(s));
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/server/IChunkLoader.java b/src/main/java/net/minecraft/server/IChunkLoader.java
|
|
index f0a052eec2..2f95174fcc 100644
|
|
--- a/src/main/java/net/minecraft/server/IChunkLoader.java
|
|
+++ b/src/main/java/net/minecraft/server/IChunkLoader.java
|
|
@@ -8,7 +8,7 @@ import javax.annotation.Nullable;
|
|
|
|
public class IChunkLoader implements AutoCloseable {
|
|
|
|
- private final IOWorker a;
|
|
+ private final IOWorker a; public IOWorker getIOWorker() { return a; } // Paper - OBFHELPER
|
|
protected final DataFixer b;
|
|
@Nullable
|
|
private PersistentStructureLegacy c;
|
|
diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java
|
|
index 3d255b1964..040d4b41ea 100644
|
|
--- a/src/main/java/net/minecraft/server/PlayerChunk.java
|
|
+++ b/src/main/java/net/minecraft/server/PlayerChunk.java
|
|
@@ -114,6 +114,19 @@ public class PlayerChunk {
|
|
Either<IChunkAccess, PlayerChunk.Failure> either = (Either<IChunkAccess, PlayerChunk.Failure>) statusFuture.getNow(null);
|
|
return either == null ? null : (Chunk) either.left().orElse(null);
|
|
}
|
|
+
|
|
+ public IChunkAccess getAvailableChunkNow() {
|
|
+ // TODO can we just getStatusFuture(EMPTY)?
|
|
+ for (ChunkStatus curr = ChunkStatus.FULL, next = curr.getPreviousStatus(); curr != next; curr = next, next = next.getPreviousStatus()) {
|
|
+ CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> future = this.getStatusFutureUnchecked(curr);
|
|
+ Either<IChunkAccess, PlayerChunk.Failure> either = future.getNow(null);
|
|
+ if (either == null || !either.left().isPresent()) {
|
|
+ continue;
|
|
+ }
|
|
+ return either.left().get();
|
|
+ }
|
|
+ return null;
|
|
+ }
|
|
// Paper end
|
|
|
|
public CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> getStatusFutureUnchecked(ChunkStatus chunkstatus) {
|
|
diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java
|
|
index 18c71609c0..57b1341b5a 100644
|
|
--- a/src/main/java/net/minecraft/server/PlayerChunkMap.java
|
|
+++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java
|
|
@@ -968,12 +968,62 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
|
}
|
|
|
|
@Nullable
|
|
- private NBTTagCompound readChunkData(ChunkCoordIntPair chunkcoordintpair) throws IOException {
|
|
+ public NBTTagCompound readChunkData(ChunkCoordIntPair chunkcoordintpair) throws IOException { // Paper - private -> public
|
|
NBTTagCompound nbttagcompound = this.read(chunkcoordintpair);
|
|
|
|
- return nbttagcompound == null ? null : this.getChunkData(this.world.getWorldProvider().getDimensionManager(), this.l, nbttagcompound, chunkcoordintpair, world); // CraftBukkit
|
|
+ // Paper start - Cache chunk status on disk
|
|
+ if (nbttagcompound == null) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ nbttagcompound = this.getChunkData(this.world.getWorldProvider().getDimensionManager(), this.l, nbttagcompound, chunkcoordintpair, world); // CraftBukkit
|
|
+ if (nbttagcompound == null) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ this.updateChunkStatusOnDisk(chunkcoordintpair, nbttagcompound);
|
|
+
|
|
+ return nbttagcompound;
|
|
+ // Paper end
|
|
+ }
|
|
+
|
|
+ // Paper start - chunk status cache "api"
|
|
+ public ChunkStatus getChunkStatusOnDiskIfCached(ChunkCoordIntPair chunkPos) {
|
|
+ RegionFile regionFile = this.getIOWorker().getRegionFileCache().getRegionFileIfLoaded(chunkPos);
|
|
+
|
|
+ return regionFile == null ? null : regionFile.getStatusIfCached(chunkPos.x, chunkPos.z);
|
|
}
|
|
|
|
+ public ChunkStatus getChunkStatusOnDisk(ChunkCoordIntPair chunkPos) throws IOException {
|
|
+ RegionFile regionFile = this.getIOWorker().getRegionFileCache().getFile(chunkPos, false);
|
|
+
|
|
+ if (!regionFile.chunkExists(chunkPos)) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ ChunkStatus status = regionFile.getStatusIfCached(chunkPos.x, chunkPos.z);
|
|
+
|
|
+ if (status != null) {
|
|
+ return status;
|
|
+ }
|
|
+
|
|
+ this.readChunkData(chunkPos);
|
|
+
|
|
+ return regionFile.getStatusIfCached(chunkPos.x, chunkPos.z);
|
|
+ }
|
|
+
|
|
+ public void updateChunkStatusOnDisk(ChunkCoordIntPair chunkPos, @Nullable NBTTagCompound compound) throws IOException {
|
|
+ RegionFile regionFile = this.getIOWorker().getRegionFileCache().getFile(chunkPos, false);
|
|
+
|
|
+ regionFile.setStatus(chunkPos.x, chunkPos.z, ChunkRegionLoader.getStatus(compound));
|
|
+ }
|
|
+
|
|
+ public IChunkAccess getUnloadingChunk(int chunkX, int chunkZ) {
|
|
+ PlayerChunk chunkHolder = this.pendingUnload.get(ChunkCoordIntPair.pair(chunkX, chunkZ));
|
|
+ return chunkHolder == null ? null : chunkHolder.getAvailableChunkNow();
|
|
+ }
|
|
+ // Paper end
|
|
+
|
|
boolean isOutsideOfRange(ChunkCoordIntPair chunkcoordintpair) {
|
|
// Spigot start
|
|
return isOutsideOfRange(chunkcoordintpair, false);
|
|
diff --git a/src/main/java/net/minecraft/server/RegionFile.java b/src/main/java/net/minecraft/server/RegionFile.java
|
|
index 6b543f89d4..d37abf2cf3 100644
|
|
--- a/src/main/java/net/minecraft/server/RegionFile.java
|
|
+++ b/src/main/java/net/minecraft/server/RegionFile.java
|
|
@@ -36,6 +36,30 @@ public class RegionFile implements AutoCloseable {
|
|
private final RegionFileBitSet freeSectors;
|
|
public final File file;
|
|
|
|
+ // Paper start - Cache chunk status
|
|
+ private final ChunkStatus[] statuses = new ChunkStatus[32 * 32];
|
|
+
|
|
+ private boolean closed;
|
|
+
|
|
+ // invoked on write/read
|
|
+ public void setStatus(int x, int z, ChunkStatus status) {
|
|
+ if (this.closed) {
|
|
+ // We've used an invalid region file.
|
|
+ throw new IllegalStateException("RegionFile is closed");
|
|
+ }
|
|
+ this.statuses[this.getChunkLocation(new ChunkCoordIntPair(x, z))] = status;
|
|
+ }
|
|
+
|
|
+ public 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 = this.getChunkLocation(new ChunkCoordIntPair(x, z));
|
|
+ return this.statuses[location];
|
|
+ }
|
|
+ // Paper end
|
|
+
|
|
public RegionFile(File file, File file1) throws IOException {
|
|
this(file.toPath(), file1.toPath(), RegionFileCompression.b);
|
|
}
|
|
@@ -350,11 +374,13 @@ public class RegionFile implements AutoCloseable {
|
|
return this.getOffset(chunkcoordintpair) != 0;
|
|
}
|
|
|
|
+ private final int getChunkLocation(ChunkCoordIntPair chunkcoordintpair) { return this.g(chunkcoordintpair); } // Paper - OBFHELPER
|
|
private static int g(ChunkCoordIntPair chunkcoordintpair) {
|
|
return chunkcoordintpair.j() + chunkcoordintpair.k() * 32;
|
|
}
|
|
|
|
public void close() throws IOException {
|
|
+ this.closed = true; // Paper
|
|
try {
|
|
this.c();
|
|
} finally {
|
|
diff --git a/src/main/java/net/minecraft/server/RegionFileCache.java b/src/main/java/net/minecraft/server/RegionFileCache.java
|
|
index b3d1bb5fd8..e07ae98540 100644
|
|
--- a/src/main/java/net/minecraft/server/RegionFileCache.java
|
|
+++ b/src/main/java/net/minecraft/server/RegionFileCache.java
|
|
@@ -18,7 +18,14 @@ public final class RegionFileCache implements AutoCloseable {
|
|
this.b = file;
|
|
}
|
|
|
|
- private RegionFile getFile(ChunkCoordIntPair chunkcoordintpair, boolean existingOnly) throws IOException { // CraftBukkit
|
|
+
|
|
+ // Paper start
|
|
+ public RegionFile getRegionFileIfLoaded(ChunkCoordIntPair chunkcoordintpair) {
|
|
+ return this.cache.getAndMoveToFirst(ChunkCoordIntPair.pair(chunkcoordintpair.getRegionX(), chunkcoordintpair.getRegionZ()));
|
|
+ }
|
|
+
|
|
+ // Paper end
|
|
+ public RegionFile getFile(ChunkCoordIntPair chunkcoordintpair, boolean existingOnly) throws IOException { // CraftBukkit // Paper - private > public
|
|
long i = ChunkCoordIntPair.pair(chunkcoordintpair.getRegionX(), chunkcoordintpair.getRegionZ());
|
|
RegionFile regionfile = (RegionFile) this.cache.getAndMoveToFirst(i);
|
|
|
|
@@ -160,7 +167,8 @@ public final class RegionFileCache implements AutoCloseable {
|
|
|
|
try {
|
|
NBTCompressedStreamTools.a(nbttagcompound, (DataOutput) dataoutputstream);
|
|
- regionfile.setOversized(chunkcoordintpair.x, chunkcoordintpair.z, false); // We don't do this anymore
|
|
+ regionfile.setStatus(chunkcoordintpair.x, chunkcoordintpair.z, ChunkRegionLoader.getStatus(nbttagcompound)); // Paper - cache status on disk
|
|
+ regionfile.setOversized(chunkcoordintpair.x, chunkcoordintpair.z, false);
|
|
} catch (Throwable throwable1) {
|
|
throwable = throwable1;
|
|
throw throwable1;
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
index 661a89c5d6..a71bb86508 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
@@ -18,6 +18,7 @@ import java.util.Objects;
|
|
import java.util.Random;
|
|
import java.util.Set;
|
|
import java.util.UUID;
|
|
+import java.util.concurrent.CompletableFuture;
|
|
import java.util.function.Predicate;
|
|
import java.util.stream.Collectors;
|
|
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
|
@@ -408,8 +409,22 @@ public class CraftWorld implements World {
|
|
|
|
@Override
|
|
public boolean isChunkGenerated(int x, int z) {
|
|
+ // Paper start - Fix this method
|
|
+ if (!Bukkit.isPrimaryThread()) {
|
|
+ return CompletableFuture.supplyAsync(() -> {
|
|
+ return CraftWorld.this.isChunkGenerated(x, z);
|
|
+ }, world.getChunkProvider().serverThreadQueue).join();
|
|
+ }
|
|
+ IChunkAccess chunk = world.getChunkProvider().getChunkAtImmediately(x, z);
|
|
+ if (chunk == null) {
|
|
+ chunk = world.getChunkProvider().playerChunkMap.getUnloadingChunk(x, z);
|
|
+ }
|
|
+ if (chunk != null) {
|
|
+ return chunk instanceof ProtoChunkExtension || chunk instanceof net.minecraft.server.Chunk;
|
|
+ }
|
|
try {
|
|
- return world.getChunkProvider().getChunkAtIfCachedImmediately(x, z) != null || world.getChunkProvider().playerChunkMap.read(new ChunkCoordIntPair(x, z)) != null; // Paper (TODO check if the first part can be removed)
|
|
+ return world.getChunkProvider().playerChunkMap.getChunkStatusOnDisk(new ChunkCoordIntPair(x, z)) == ChunkStatus.FULL;
|
|
+ // Paper end
|
|
} catch (IOException ex) {
|
|
throw new RuntimeException(ex);
|
|
}
|
|
@@ -521,20 +536,49 @@ public class CraftWorld implements World {
|
|
@Override
|
|
public boolean loadChunk(int x, int z, boolean generate) {
|
|
org.spigotmc.AsyncCatcher.catchOp("chunk load"); // Spigot
|
|
- IChunkAccess chunk = world.getChunkProvider().getChunkAt(x, z, generate || isChunkGenerated(x, z) ? ChunkStatus.FULL : ChunkStatus.EMPTY, true); // Paper
|
|
+ // Paper start - Optimize this method
|
|
+ ChunkCoordIntPair chunkPos = new ChunkCoordIntPair(x, z);
|
|
|
|
- // If generate = false, but the chunk already exists, we will get this back.
|
|
- if (chunk instanceof ProtoChunkExtension) {
|
|
- // We then cycle through again to get the full chunk immediately, rather than after the ticket addition
|
|
- chunk = world.getChunkProvider().getChunkAt(x, z, ChunkStatus.FULL, true);
|
|
- }
|
|
+ if (!generate) {
|
|
|
|
- if (chunk instanceof net.minecraft.server.Chunk) {
|
|
- world.getChunkProvider().addTicket(TicketType.PLUGIN, new ChunkCoordIntPair(x, z), 1, Unit.INSTANCE);
|
|
- return true;
|
|
+ IChunkAccess immediate = world.getChunkProvider().getChunkAtImmediately(x, z);
|
|
+ if (immediate == null) {
|
|
+ immediate = world.getChunkProvider().playerChunkMap.getUnloadingChunk(x, z);
|
|
+ }
|
|
+ if (immediate != null) {
|
|
+ if (!(immediate instanceof ProtoChunkExtension) && !(immediate instanceof net.minecraft.server.Chunk)) {
|
|
+ return false; // not full status
|
|
+ }
|
|
+ world.getChunkProvider().addTicket(TicketType.PLUGIN, chunkPos, 1, Unit.INSTANCE);
|
|
+ world.getChunkAt(x, z); // make sure we're at ticket level 32 or lower
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ net.minecraft.server.RegionFile file;
|
|
+ try {
|
|
+ file = world.getChunkProvider().playerChunkMap.getIOWorker().getRegionFileCache().getFile(chunkPos, false);
|
|
+ } catch (IOException ex) {
|
|
+ throw new RuntimeException(ex);
|
|
+ }
|
|
+
|
|
+ ChunkStatus status = file.getStatusIfCached(x, z);
|
|
+ if (!file.chunkExists(chunkPos) || (status != null && status != ChunkStatus.FULL)) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ IChunkAccess chunk = world.getChunkProvider().getChunkAt(x, z, ChunkStatus.EMPTY, true);
|
|
+ if (!(chunk instanceof ProtoChunkExtension) && !(chunk instanceof net.minecraft.server.Chunk)) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ // fall through to load
|
|
+ // we do this so we do not re-read the chunk data on disk
|
|
}
|
|
|
|
- return false;
|
|
+ world.getChunkProvider().addTicket(TicketType.PLUGIN, chunkPos, 1, Unit.INSTANCE);
|
|
+ world.getChunkProvider().getChunkAt(x, z, ChunkStatus.FULL, true);
|
|
+ return true;
|
|
+ // Paper end
|
|
}
|
|
|
|
@Override
|
|
--
|
|
2.25.1
|
|
|