mirror of
https://hub.spigotmc.org/stash/scm/spigot/spigot.git
synced 2025-08-31 21:49:13 +00:00
366 lines
16 KiB
Diff
366 lines
16 KiB
Diff
From 07d10e70dcd4930297ac817761eadabb24eca246 Mon Sep 17 00:00:00 2001
|
|
From: Geoff Crossland <gcrossland+bukkit@gmail.com>
|
|
Date: Fri, 11 Aug 2017 19:23:58 +0100
|
|
Subject: [PATCH] Make unloaded chunk saving more asynchronous
|
|
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java
|
|
index d35203cc5..0296d3ef0 100644
|
|
--- a/src/main/java/net/minecraft/server/ChunkProviderServer.java
|
|
+++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java
|
|
@@ -213,17 +213,17 @@ public class ChunkProviderServer implements IChunkProvider {
|
|
|
|
public void saveChunkNOP(Chunk chunk) {
|
|
try {
|
|
- this.chunkLoader.a(this.world, chunk);
|
|
+ // this.chunkLoader.a(this.world, chunk); // Spigot
|
|
} catch (Exception exception) {
|
|
ChunkProviderServer.a.error("Couldn\'t save entities", exception);
|
|
}
|
|
|
|
}
|
|
|
|
- public void saveChunk(IChunkAccess ichunkaccess) {
|
|
+ public void saveChunk(IChunkAccess ichunkaccess, boolean unloaded) { // Spigot
|
|
try {
|
|
ichunkaccess.setLastSaved(this.world.getTime());
|
|
- this.chunkLoader.saveChunk(this.world, ichunkaccess);
|
|
+ this.chunkLoader.saveChunk(this.world, ichunkaccess, unloaded); // Spigot
|
|
} catch (IOException ioexception) {
|
|
ChunkProviderServer.a.error("Couldn\'t save chunk", ioexception);
|
|
} catch (ExceptionWorldConflict exceptionworldconflict) {
|
|
@@ -247,7 +247,7 @@ public class ChunkProviderServer implements IChunkProvider {
|
|
}
|
|
|
|
if (chunk.c(flag)) {
|
|
- this.saveChunk(chunk);
|
|
+ this.saveChunk(chunk, false); // Spigot
|
|
chunk.a(false);
|
|
++i;
|
|
if (i == 24 && !flag && false) { // Spigot
|
|
@@ -341,7 +341,7 @@ public class ChunkProviderServer implements IChunkProvider {
|
|
// Moved from unloadChunks above
|
|
chunk.removeEntities();
|
|
if (save) {
|
|
- this.saveChunk(chunk);
|
|
+ this.saveChunk(chunk, true); // Spigot
|
|
this.saveChunkNOP(chunk);
|
|
}
|
|
this.chunks.remove(chunk.chunkKey);
|
|
diff --git a/src/main/java/net/minecraft/server/ChunkRegionLoader.java b/src/main/java/net/minecraft/server/ChunkRegionLoader.java
|
|
index 6a99c9928..88301ee61 100644
|
|
--- a/src/main/java/net/minecraft/server/ChunkRegionLoader.java
|
|
+++ b/src/main/java/net/minecraft/server/ChunkRegionLoader.java
|
|
@@ -26,15 +26,20 @@ import java.util.function.Predicate;
|
|
import javax.annotation.Nullable;
|
|
import org.apache.logging.log4j.LogManager;
|
|
import org.apache.logging.log4j.Logger;
|
|
+// Spigot start
|
|
+import java.util.function.Supplier;
|
|
+import org.spigotmc.SupplierUtils;
|
|
+// Spigot end
|
|
|
|
public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver {
|
|
|
|
private static final Logger a = LogManager.getLogger();
|
|
- private final Object2ObjectMap<ChunkCoordIntPair, NBTTagCompound> b = Object2ObjectMaps.synchronize(new Object2ObjectLinkedOpenHashMap());
|
|
+ private final Object2ObjectMap<ChunkCoordIntPair, Supplier<NBTTagCompound>> b = Object2ObjectMaps.synchronize(new Object2ObjectLinkedOpenHashMap()); // Spigot
|
|
private final File c;
|
|
private final DataFixer d;
|
|
private PersistentStructureLegacy e;
|
|
// private boolean f; // CraftBukkit
|
|
+ private static final double SAVE_QUEUE_TARGET_SIZE = 625; // Spigot
|
|
|
|
public ChunkRegionLoader(File file, DataFixer datafixer) {
|
|
this.c = file;
|
|
@@ -43,7 +48,7 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver {
|
|
|
|
@Nullable
|
|
private synchronized NBTTagCompound a(GeneratorAccess generatoraccess, int i, int j) throws IOException {
|
|
- NBTTagCompound nbttagcompound = (NBTTagCompound) this.b.get(new ChunkCoordIntPair(i, j));
|
|
+ NBTTagCompound nbttagcompound = SupplierUtils.getIfExists(this.b.get(new ChunkCoordIntPair(i, j))); // Spigot
|
|
|
|
return nbttagcompound != null ? nbttagcompound : this.a(generatoraccess.o().getDimensionManager(), generatoraccess.s_(), i, j, generatoraccess); // CraftBukkit
|
|
}
|
|
@@ -110,7 +115,7 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver {
|
|
nbttagcompound = GameProfileSerializer.a(this.d, DataFixTypes.CHUNK, nbttagcompound, Math.max(1493, k));
|
|
if (k < 1519) {
|
|
nbttagcompound.setInt("DataVersion", 1519);
|
|
- this.a(new ChunkCoordIntPair(i, j), nbttagcompound);
|
|
+ this.a(new ChunkCoordIntPair(i, j), new SupplierUtils.ValueSupplier<>(nbttagcompound)); // Spigot
|
|
}
|
|
|
|
return nbttagcompound;
|
|
@@ -175,7 +180,7 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver {
|
|
|
|
public synchronized boolean chunkExists(int i, int j) {
|
|
ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(i, j);
|
|
- NBTTagCompound nbttagcompound = (NBTTagCompound) this.b.get(chunkcoordintpair);
|
|
+ Supplier<NBTTagCompound> nbttagcompound = this.b.get(chunkcoordintpair); // Spigot
|
|
|
|
return nbttagcompound != null ? true : RegionFileCache.chunkExists(this.c, i, j);
|
|
}
|
|
@@ -248,7 +253,13 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver {
|
|
}
|
|
}
|
|
|
|
+ // Spigot start
|
|
public synchronized void saveChunk(World world, IChunkAccess ichunkaccess) throws IOException, ExceptionWorldConflict {
|
|
+ saveChunk(world, ichunkaccess, false); // Ideally we shouldn't use this, but easier than decompile errors
|
|
+ }
|
|
+
|
|
+ public synchronized void saveChunk(World world, IChunkAccess ichunkaccess, boolean unloaded) throws IOException, ExceptionWorldConflict {
|
|
+ // Spigot end
|
|
world.checkSession();
|
|
|
|
try {
|
|
@@ -259,8 +270,19 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver {
|
|
ChunkCoordIntPair chunkcoordintpair = ichunkaccess.getPos();
|
|
|
|
nbttagcompound.set("Level", nbttagcompound1);
|
|
+ // Spigot start
|
|
+ Supplier<NBTTagCompound> completion;
|
|
+ final long worldTime = world.getTime();
|
|
+ final boolean worldHasSkyLight = world.worldProvider.g();
|
|
if (ichunkaccess.i().d() == ChunkStatus.Type.LEVELCHUNK) {
|
|
- this.saveBody((Chunk) ichunkaccess, world, nbttagcompound1);
|
|
+ final Chunk chunk = (Chunk) ichunkaccess;
|
|
+ saveEntities(nbttagcompound1, chunk, world);
|
|
+ completion = new Supplier<NBTTagCompound>() {
|
|
+ public NBTTagCompound get() {
|
|
+ saveBody(chunk, world, nbttagcompound1, worldTime, worldHasSkyLight);
|
|
+ return nbttagcompound;
|
|
+ }
|
|
+ };
|
|
} else {
|
|
NBTTagCompound nbttagcompound2 = this.a(world, chunkcoordintpair.x, chunkcoordintpair.z);
|
|
|
|
@@ -268,17 +290,23 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver {
|
|
return;
|
|
}
|
|
|
|
- this.a((ProtoChunk) ichunkaccess, world, nbttagcompound1);
|
|
+ completion = new Supplier<NBTTagCompound>() {
|
|
+ public NBTTagCompound get() {
|
|
+ a((ProtoChunk) ichunkaccess, world, nbttagcompound1, worldTime, worldHasSkyLight);
|
|
+ return nbttagcompound;
|
|
+ }
|
|
+ };
|
|
}
|
|
|
|
- this.a(chunkcoordintpair, nbttagcompound);
|
|
+ this.a(chunkcoordintpair, SupplierUtils.createUnivaluedSupplier(completion, unloaded && this.b.size() < SAVE_QUEUE_TARGET_SIZE));
|
|
+ // Spigot end
|
|
} catch (Exception exception) {
|
|
ChunkRegionLoader.a.error("Failed to save chunk", exception);
|
|
}
|
|
|
|
}
|
|
|
|
- protected synchronized void a(ChunkCoordIntPair chunkcoordintpair, NBTTagCompound nbttagcompound) {
|
|
+ protected synchronized void a(ChunkCoordIntPair chunkcoordintpair, Supplier<NBTTagCompound> nbttagcompound) { // Spigot
|
|
this.b.put(chunkcoordintpair, nbttagcompound);
|
|
FileIOThread.a().a(this);
|
|
}
|
|
@@ -289,7 +317,7 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver {
|
|
}
|
|
|
|
private synchronized boolean processSaveQueueEntry(boolean logCompletion) {
|
|
- Iterator<Map.Entry<ChunkCoordIntPair, NBTTagCompound>> iter = this.b.entrySet().iterator();
|
|
+ Iterator<Map.Entry<ChunkCoordIntPair, Supplier<NBTTagCompound>>> iter = this.b.entrySet().iterator(); // Spigot
|
|
if (!iter.hasNext()) {
|
|
if (logCompletion) {
|
|
// CraftBukkit end
|
|
@@ -299,15 +327,16 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver {
|
|
return false;
|
|
} else {
|
|
// CraftBukkit start
|
|
- Map.Entry<ChunkCoordIntPair, NBTTagCompound> entry = iter.next();
|
|
+ Map.Entry<ChunkCoordIntPair, Supplier<NBTTagCompound>> entry = iter.next(); // Spigot
|
|
ChunkCoordIntPair chunkcoordintpair = entry.getKey();
|
|
- NBTTagCompound nbttagcompound = entry.getValue();
|
|
+ Supplier<NBTTagCompound> value = entry.getValue(); // Spigot
|
|
// CraftBukkit end
|
|
|
|
boolean flag;
|
|
|
|
try {
|
|
// NBTTagCompound nbttagcompound = (NBTTagCompound) this.b.get(chunkcoordintpair); // CraftBukkit
|
|
+ NBTTagCompound nbttagcompound = SupplierUtils.getIfExists(value); // Spigot
|
|
|
|
if (nbttagcompound != null) {
|
|
try {
|
|
@@ -319,7 +348,7 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver {
|
|
|
|
flag = true;
|
|
} finally {
|
|
- this.b.remove(chunkcoordintpair, nbttagcompound); // CraftBukkit
|
|
+ this.b.remove(chunkcoordintpair, value); // CraftBukkit // Spigot
|
|
}
|
|
|
|
return flag;
|
|
@@ -373,13 +402,13 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver {
|
|
|
|
}
|
|
|
|
- private void a(ProtoChunk protochunk, World world, NBTTagCompound nbttagcompound) {
|
|
+ private void a(ProtoChunk protochunk, World world, NBTTagCompound nbttagcompound, long worldTime, boolean worldHasSkyLight) { // Spigot
|
|
int i = protochunk.getPos().x;
|
|
int j = protochunk.getPos().z;
|
|
|
|
nbttagcompound.setInt("xPos", i);
|
|
nbttagcompound.setInt("zPos", j);
|
|
- nbttagcompound.setLong("LastUpdate", world.getTime());
|
|
+ nbttagcompound.setLong("LastUpdate", worldTime); // Spigot
|
|
nbttagcompound.setLong("InhabitedTime", protochunk.m());
|
|
nbttagcompound.setString("Status", protochunk.i().b());
|
|
ChunkConverter chunkconverter = protochunk.v();
|
|
@@ -389,7 +418,7 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver {
|
|
}
|
|
|
|
ChunkSection[] achunksection = protochunk.getSections();
|
|
- NBTTagList nbttaglist = this.a(world, achunksection);
|
|
+ NBTTagList nbttaglist = this.a(world, achunksection, worldHasSkyLight); // Spigot
|
|
|
|
nbttagcompound.set("Sections", nbttaglist);
|
|
BiomeBase[] abiomebase = protochunk.getBiomeIndex();
|
|
@@ -459,10 +488,10 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver {
|
|
nbttagcompound.set("Structures", this.a(i, j, protochunk.e(), protochunk.f()));
|
|
}
|
|
|
|
- private void saveBody(Chunk chunk, World world, NBTTagCompound nbttagcompound) {
|
|
+ private void saveBody(Chunk chunk, World world, NBTTagCompound nbttagcompound, long worldTime, boolean worldHasSkyLight) { // Spigot
|
|
nbttagcompound.setInt("xPos", chunk.locX);
|
|
nbttagcompound.setInt("zPos", chunk.locZ);
|
|
- nbttagcompound.setLong("LastUpdate", world.getTime());
|
|
+ nbttagcompound.setLong("LastUpdate", worldTime); // Spigot
|
|
nbttagcompound.setLong("InhabitedTime", chunk.m());
|
|
nbttagcompound.setString("Status", chunk.i().b());
|
|
ChunkConverter chunkconverter = chunk.F();
|
|
@@ -472,7 +501,7 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver {
|
|
}
|
|
|
|
ChunkSection[] achunksection = chunk.getSections();
|
|
- NBTTagList nbttaglist = this.a(world, achunksection);
|
|
+ NBTTagList nbttaglist = this.a(world, achunksection, worldHasSkyLight); // Spigot
|
|
|
|
nbttagcompound.set("Sections", nbttaglist);
|
|
BiomeBase[] abiomebase = chunk.getBiomeIndex();
|
|
@@ -483,6 +512,12 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver {
|
|
}
|
|
|
|
nbttagcompound.setIntArray("Biomes", aint);
|
|
+
|
|
+ // Spigot start - End this method here and split off entity saving to another method
|
|
+ }
|
|
+
|
|
+ private void saveEntities(NBTTagCompound nbttagcompound, Chunk chunk, World world) {
|
|
+ // Spigot end
|
|
chunk.f(false);
|
|
NBTTagList nbttaglist1 = new NBTTagList();
|
|
|
|
@@ -788,9 +823,9 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver {
|
|
return protochunk;
|
|
}
|
|
|
|
- private NBTTagList a(World world, ChunkSection[] achunksection) {
|
|
+ private NBTTagList a(World world, ChunkSection[] achunksection, boolean worldHasSkyLight) { // Spigot
|
|
NBTTagList nbttaglist = new NBTTagList();
|
|
- boolean flag = world.worldProvider.g();
|
|
+ boolean flag = worldHasSkyLight; // Spigot
|
|
ChunkSection[] achunksection1 = achunksection;
|
|
int i = achunksection.length;
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/IChunkLoader.java b/src/main/java/net/minecraft/server/IChunkLoader.java
|
|
index f761bb90f..a2b2f46e1 100644
|
|
--- a/src/main/java/net/minecraft/server/IChunkLoader.java
|
|
+++ b/src/main/java/net/minecraft/server/IChunkLoader.java
|
|
@@ -14,7 +14,9 @@ public interface IChunkLoader {
|
|
|
|
void saveChunk(World world, IChunkAccess ichunkaccess) throws IOException, ExceptionWorldConflict;
|
|
|
|
- void a(IBlockAccess iblockaccess, Chunk chunk) throws IOException;
|
|
+ void saveChunk(World world, IChunkAccess ichunkaccess, boolean unloaded) throws IOException, ExceptionWorldConflict; // Spigot
|
|
+
|
|
+ // void a(IBlockAccess iblockaccess, Chunk chunk) throws IOException; // Spigot
|
|
|
|
void b();
|
|
|
|
diff --git a/src/main/java/org/spigotmc/SupplierUtils.java b/src/main/java/org/spigotmc/SupplierUtils.java
|
|
new file mode 100644
|
|
index 000000000..f63ce98bf
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/spigotmc/SupplierUtils.java
|
|
@@ -0,0 +1,69 @@
|
|
+package org.spigotmc;
|
|
+
|
|
+import java.util.function.Supplier;
|
|
+import javax.annotation.Nullable;
|
|
+
|
|
+/**
|
|
+ * Utilities for creating and working with {@code Supplier} instances.
|
|
+ */
|
|
+public class SupplierUtils {
|
|
+
|
|
+ /**
|
|
+ * Repeatedly supplies the first value from a given sequence; the value is
|
|
+ * obtained only when required.
|
|
+ */
|
|
+ public static class LazyHeadSupplier<V> implements Supplier<V> {
|
|
+
|
|
+ private @Nullable Supplier<V> completion;
|
|
+ private @Nullable V value;
|
|
+
|
|
+ public LazyHeadSupplier(Supplier<V> completion) {
|
|
+ this.completion = completion;
|
|
+ }
|
|
+
|
|
+ public synchronized @Nullable V get() {
|
|
+ if (this.completion != null) {
|
|
+ this.value = this.completion.get();
|
|
+ this.completion = null;
|
|
+ }
|
|
+
|
|
+ return this.value;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Repeatedly supplies the given value.
|
|
+ */
|
|
+ public static class ValueSupplier<V> implements Supplier<V> {
|
|
+
|
|
+ private final @Nullable V value;
|
|
+
|
|
+ public ValueSupplier(@Nullable V value) {
|
|
+ this.value = value;
|
|
+ }
|
|
+
|
|
+ public @Nullable V get() {
|
|
+ return this.value;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Creates a new {@code Supplier} that supplies the given {@code Supplier}'s
|
|
+ * first value.
|
|
+ *
|
|
+ * @param doLazily {@code false}, if {@code completion.get()} should be
|
|
+ * called immediately, or {@code true}, if {@code completion.get()} should
|
|
+ * be called only when the value is first needed.
|
|
+ */
|
|
+ public static <V> Supplier<V> createUnivaluedSupplier(Supplier<V> completion, boolean doLazily) {
|
|
+ return doLazily ? new LazyHeadSupplier<V>(completion) : new ValueSupplier<V>(completion.get());
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Returns {@code supplier.get()}, if {@code supplier} is non-{@code null}
|
|
+ * (or {@code null}, otherwise).
|
|
+ */
|
|
+ public static @Nullable <V> V getIfExists(@Nullable Supplier<V> supplier) {
|
|
+ return supplier != null ? supplier.get() : null;
|
|
+ }
|
|
+}
|
|
--
|
|
2.17.1
|
|
|