PaperMC/patches/server/1034-Write-SavedData-IO-async.patch

165 lines
8.5 KiB
Diff
Raw Normal View History

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Cryptite <cryptite@gmail.com>
Date: Tue, 27 Jun 2023 11:35:52 -0500
Subject: [PATCH] Write SavedData IO async
Co-Authored-By: Shane Freeder <theboyetronic@gmail.com>
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
index 4a5dc7fd4eb1a7ab1ec371f0f107de882f88149c..dcb5651d1d9b10b40430fb2f713beedf68336704 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
@@ -369,6 +369,13 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
2024-07-12 17:58:54 +02:00
public void close(boolean save) throws IOException {
((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().chunkHolderManager.close(save, true); // Paper - rewrite chunk system
+ // Paper start - Write SavedData IO async
+ try {
+ this.dataStorage.close();
2024-07-12 17:58:54 +02:00
+ } catch (final IOException e) {
+ LOGGER.error("Failed to close persistent world data", e);
+ }
+ // Paper end - Write SavedData IO async
}
// CraftBukkit start - modelled on below
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
index 6d8a5c2b0d95828abbd99914d1d308522b89529f..e50df5a1f3b89b3d0687d6584bdd977f8b71a3f6 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -1330,7 +1330,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ca.spottedleaf.
progressListener.progressStartNoAbort(Component.translatable("menu.savingLevel"));
}
- this.saveLevelData();
+ this.saveLevelData(!close); // Paper - Write SavedData IO async
if (progressListener != null) {
progressListener.progressStage(Component.translatable("menu.savingChunks"));
}
@@ -1361,12 +1361,12 @@ public class ServerLevel extends Level implements WorldGenLevel, ca.spottedleaf.
// CraftBukkit end
}
- private void saveLevelData() {
+ private void saveLevelData(boolean async) { // Paper - Write SavedData IO async
if (this.dragonFight != null) {
this.serverLevelData.setEndDragonFightData(this.dragonFight.saveData()); // CraftBukkit
}
- this.getChunkSource().getDataStorage().save();
+ this.getChunkSource().getDataStorage().save(async); // Paper - Write SavedData IO async
}
public <T extends Entity> List<? extends T> getEntities(EntityTypeTest<Entity, T> filter, Predicate<? super T> predicate) {
diff --git a/src/main/java/net/minecraft/util/worldupdate/WorldUpgrader.java b/src/main/java/net/minecraft/util/worldupdate/WorldUpgrader.java
2024-07-12 17:58:54 +02:00
index 0382b6597a130d746f8954a93a756a9d1ac81d50..cb39c629af1827078f35904a373d35a63fea17ff 100644
--- a/src/main/java/net/minecraft/util/worldupdate/WorldUpgrader.java
+++ b/src/main/java/net/minecraft/util/worldupdate/WorldUpgrader.java
2024-04-25 13:02:27 +02:00
@@ -116,7 +116,13 @@ public class WorldUpgrader {
(new WorldUpgrader.PoiUpgrader(this)).upgrade();
WorldUpgrader.LOGGER.info("Upgrading blocks");
(new WorldUpgrader.ChunkUpgrader()).upgrade();
- this.overworldDataStorage.save();
+ // Paper start - Write SavedData IO async
+ try {
+ this.overworldDataStorage.close();
+ } catch (final IOException e) {
+ LOGGER.error("Failed to close persistent world data", e);
+ }
+ // Paper end - Write SavedData IO async
i = Util.getMillis() - i;
WorldUpgrader.LOGGER.info("World optimizaton finished after {} seconds", i / 1000L);
this.finished = true;
diff --git a/src/main/java/net/minecraft/world/level/saveddata/SavedData.java b/src/main/java/net/minecraft/world/level/saveddata/SavedData.java
2024-04-25 13:02:27 +02:00
index 9cc3850bb70dfbcf342651360314a19fd9ea3ecc..4cbb943b702baaeb8bfd2b558cc848e719cf095d 100644
--- a/src/main/java/net/minecraft/world/level/saveddata/SavedData.java
+++ b/src/main/java/net/minecraft/world/level/saveddata/SavedData.java
2024-04-25 13:02:27 +02:00
@@ -30,20 +30,36 @@ public abstract class SavedData {
return this.dirty;
}
2024-04-25 13:02:27 +02:00
+ // Paper start - Write SavedData IO async - joining is evil, but we assume the old blocking behavior here just for safety
+ @io.papermc.paper.annotation.DoNotUse
public void save(File file, HolderLookup.Provider registryLookup) {
+ save(file, registryLookup, null).join();
+ }
+
2024-04-25 13:02:27 +02:00
+ public java.util.concurrent.CompletableFuture<Void> save(File file, HolderLookup.Provider registryLookup, @org.jetbrains.annotations.Nullable java.util.concurrent.ExecutorService ioExecutor) {
+ // Paper end - Write SavedData IO async
if (this.isDirty()) {
CompoundTag compoundTag = new CompoundTag();
2024-04-25 13:02:27 +02:00
compoundTag.put("data", this.save(new CompoundTag(), registryLookup));
NbtUtils.addCurrentDataVersion(compoundTag);
+ Runnable writeRunnable = () -> { // Paper - Write SavedData IO async
try {
NbtIo.writeCompressed(compoundTag, file.toPath());
2024-04-25 13:02:27 +02:00
} catch (IOException var5) {
LOGGER.error("Could not save data {}", this, var5);
}
+ }; // Paper - Write SavedData IO async
this.setDirty(false);
+ // Paper start - Write SavedData IO async
+ if (ioExecutor == null) {
+ return java.util.concurrent.CompletableFuture.runAsync(writeRunnable); // No executor, just use common pool
+ }
+ return java.util.concurrent.CompletableFuture.runAsync(writeRunnable, ioExecutor);
}
+ return java.util.concurrent.CompletableFuture.completedFuture(null);
+ // Paper end - Write SavedData IO async
}
2024-04-25 13:02:27 +02:00
public static record Factory<T extends SavedData>(
diff --git a/src/main/java/net/minecraft/world/level/storage/DimensionDataStorage.java b/src/main/java/net/minecraft/world/level/storage/DimensionDataStorage.java
2024-04-25 13:02:27 +02:00
index 7d98d2911e9d6ec429ce7df1f1f2650c7ea32325..6e23e69abd56eeda3b52a22019e1b74ae10682e7 100644
--- a/src/main/java/net/minecraft/world/level/storage/DimensionDataStorage.java
+++ b/src/main/java/net/minecraft/world/level/storage/DimensionDataStorage.java
2024-04-25 13:02:27 +02:00
@@ -23,17 +23,19 @@ import net.minecraft.util.datafix.DataFixTypes;
import net.minecraft.world.level.saveddata.SavedData;
import org.slf4j.Logger;
-public class DimensionDataStorage {
+public class DimensionDataStorage implements java.io.Closeable { // Paper - Write SavedData IO async
private static final Logger LOGGER = LogUtils.getLogger();
public final Map<String, SavedData> cache = Maps.newHashMap();
private final DataFixer fixerUpper;
2024-04-25 13:02:27 +02:00
private final HolderLookup.Provider registries;
private final File dataFolder;
+ protected final java.util.concurrent.ExecutorService ioExecutor; // Paper - Write SavedData IO async
2024-04-25 13:02:27 +02:00
public DimensionDataStorage(File directory, DataFixer dataFixer, HolderLookup.Provider registryLookup) {
this.fixerUpper = dataFixer;
this.dataFolder = directory;
2024-04-25 13:02:27 +02:00
this.registries = registryLookup;
+ this.ioExecutor = java.util.concurrent.Executors.newSingleThreadExecutor(new com.google.common.util.concurrent.ThreadFactoryBuilder().setNameFormat("DimensionDataIO - " + dataFolder.getParent() + " - %d").setDaemon(true).build()); // Paper - Write SavedData IO async
}
private File getDataFile(String id) {
2024-04-25 13:02:27 +02:00
@@ -123,10 +125,23 @@ public class DimensionDataStorage {
return bl;
}
- public void save() {
+ // Paper start - Write SavedData IO async
+ @Override
+ public void close() throws IOException {
+ save(false);
+ this.ioExecutor.shutdown();
+ }
+ // Paper end - Write SavedData IO async
+
+ public void save(boolean async) { // Paper - Write SavedData IO async
this.cache.forEach((id, state) -> {
if (state != null) {
2024-04-25 13:02:27 +02:00
- state.save(this.getDataFile(id), this.registries);
+ // Paper start - Write SavedData IO async
2024-04-25 13:02:27 +02:00
+ final java.util.concurrent.CompletableFuture<Void> save = state.save(this.getDataFile(id), this.registries, this.ioExecutor);
+ if (!async) {
+ save.join();
+ }
+ // Paper end - Write SavedData IO async
}
});
}