@@ -3,157 +3,162 @@ From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com>
33Date: Fri, 23 Aug 2024 22:04:20 -0400
44Subject: [PATCH] Nitori: Async playerdata saving
55
6- Original license: GPL v3
6+ Original license: GPL-3.0
77Original project: https://github.com/Gensokyo-Reimagined/Nitori
88
9+ diff --git a/net/minecraft/server/PlayerAdvancements.java b/net/minecraft/server/PlayerAdvancements.java
10+ index 78135cf45c8900eb142933d216744f4a73127965..14e33218bb9bd3e1f8484c88114900297ec65c1e 100644
11+ --- a/net/minecraft/server/PlayerAdvancements.java
12+ +++ b/net/minecraft/server/PlayerAdvancements.java
13+ @@ -111,6 +111,7 @@ public class PlayerAdvancements {
14+
15+ private void load(ServerAdvancementManager manager) {
16+ if (Files.isRegularFile(this.playerSavePath)) {
17+ + org.dreeam.leaf.async.AsyncPlayerDataSaving.submit(playerSavePath, org.dreeam.leaf.config.modules.async.AsyncPlayerDataSave.advancements); // Leaf - Async playerdata saving
18+ try (Reader bufferedReader = Files.newBufferedReader(this.playerSavePath, StandardCharsets.UTF_8)) {
19+ JsonElement jsonElement = StrictJsonParser.parse(bufferedReader);
20+ PlayerAdvancements.Data data = this.codec.parse(JsonOps.INSTANCE, jsonElement).getOrThrow(JsonParseException::new);
21+ @@ -128,17 +129,18 @@ public class PlayerAdvancements {
22+
23+ public void save() {
24+ if (org.spigotmc.SpigotConfig.disableAdvancementSaving) return; // Spigot
25+ + // Leaf start - Async playerdata saving
26+ JsonElement jsonElement = this.codec.encodeStart(JsonOps.INSTANCE, this.asData()).getOrThrow();
27+ -
28+ - try {
29+ - FileUtil.createDirectoriesSafe(this.playerSavePath.getParent());
30+ -
31+ - try (Writer bufferedWriter = Files.newBufferedWriter(this.playerSavePath, StandardCharsets.UTF_8)) {
32+ - GSON.toJson(jsonElement, GSON.newJsonWriter(bufferedWriter));
33+ - }
34+ - } catch (JsonIOException | IOException var7) {
35+ - LOGGER.error("Couldn't save player advancements to {}", this.playerSavePath, var7);
36+ - }
37+ + Path path = this.playerSavePath;
38+ + org.dreeam.leaf.async.AsyncPlayerDataSaving.submit(() -> {
39+ + FileUtil.createDirectoriesSafe(path.getParent());
40+ + String content = GSON.toJson(jsonElement);
41+ + Path temp = org.dreeam.leaf.async.AsyncPlayerDataSaving.tempFile(path);
42+ + org.apache.commons.io.FileUtils.writeStringToFile(temp.toFile(), content, java.nio.charset.StandardCharsets.UTF_8, false);
43+ + Files.move(temp, path, java.nio.file.StandardCopyOption.REPLACE_EXISTING);
44+ + return null;
45+ + }, this.playerSavePath, org.dreeam.leaf.config.modules.async.AsyncPlayerDataSave.advancements);
46+ + // Leaf end - Async playerdata saving
47+ }
48+
49+ private void applyFrom(ServerAdvancementManager advancementManager, PlayerAdvancements.Data data) {
50+ diff --git a/net/minecraft/stats/ServerStatsCounter.java b/net/minecraft/stats/ServerStatsCounter.java
51+ index f239f445f83d50a69e7e6ffb97e7cf005c421e3a..13710f2fcceba200b6df8222d2402f33f83be0da 100644
52+ --- a/net/minecraft/stats/ServerStatsCounter.java
53+ +++ b/net/minecraft/stats/ServerStatsCounter.java
54+ @@ -68,6 +68,7 @@ public class ServerStatsCounter extends StatsCounter {
55+ if (Files.isRegularFile(file)) {
56+ try (Reader bufferedReader = Files.newBufferedReader(file, StandardCharsets.UTF_8)) {
57+ JsonElement jsonElement = StrictJsonParser.parse(bufferedReader);
58+ + org.dreeam.leaf.async.AsyncPlayerDataSaving.submit(file, org.dreeam.leaf.config.modules.async.AsyncPlayerDataSave.stats); // Leaf - Async playerdata saving
59+ this.parse(server.getFixerUpper(), jsonElement);
60+ } catch (IOException var8) {
61+ LOGGER.error("Couldn't read statistics file {}", file, var8);
62+ @@ -90,15 +91,16 @@ public class ServerStatsCounter extends StatsCounter {
63+
64+ public void save() {
65+ if (org.spigotmc.SpigotConfig.disableStatSaving) return; // Spigot
66+ - try {
67+ - FileUtil.createDirectoriesSafe(this.file.getParent());
68+ -
69+ - try (Writer bufferedWriter = Files.newBufferedWriter(this.file, StandardCharsets.UTF_8)) {
70+ - GSON.toJson(this.toJson(), GSON.newJsonWriter(bufferedWriter));
71+ - }
72+ - } catch (JsonIOException | IOException var6) {
73+ - LOGGER.error("Couldn't save stats to {}", this.file, var6);
74+ - }
75+ + // Leaf start - Async playerdata saving
76+ + Path file = this.file;
77+ + JsonElement data = this.toJson();
78+ + org.dreeam.leaf.async.AsyncPlayerDataSaving.submit(() -> {
79+ + java.nio.file.Path temp = org.dreeam.leaf.async.AsyncPlayerDataSaving.tempFile(file);
80+ + org.apache.commons.io.FileUtils.writeStringToFile(temp.toFile(), GSON.toJson(data), java.nio.charset.StandardCharsets.UTF_8, false);
81+ + java.nio.file.Files.move(temp, file, java.nio.file.StandardCopyOption.REPLACE_EXISTING);
82+ + return null;
83+ + }, file, org.dreeam.leaf.config.modules.async.AsyncPlayerDataSave.stats);
84+ + // Leaf end - Async playerdata saving
85+ }
86+
87+ @Override
988diff --git a/net/minecraft/world/level/storage/LevelStorageSource.java b/net/minecraft/world/level/storage/LevelStorageSource.java
10- index 140630186c3b0324c248cc2a2f31d3b906557b9c..d5e9190f0a6baffd2b08225f595ff4d7eb662e65 100644
89+ index 140630186c3b0324c248cc2a2f31d3b906557b9c..75feaa87159f8bf399b6d51f73ccf1c25a50265e 100644
1190--- a/net/minecraft/world/level/storage/LevelStorageSource.java
1291+++ b/net/minecraft/world/level/storage/LevelStorageSource.java
13- @@ -516,15 +516,26 @@ public class LevelStorageSource {
92+ @@ -516,15 +516,20 @@ public class LevelStorageSource {
1493 private void saveLevelData(CompoundTag tag) {
1594 Path path = this.levelDirectory.path();
1695
96+ - try {
1797+ // Leaf start - Async playerdata saving
1898+ // Save level.dat asynchronously
19- + var nbtBytes = new it.unimi.dsi.fastutil.io.FastByteArrayOutputStream(65536);
20- try {
21- - Path path1 = Files.createTempFile(path, "level", ".dat");
99+ +
100+ + Path path2 = this.levelDirectory.oldDataFile();
101+ + Path path3 = this.levelDirectory.dataFile();
102+ + org.dreeam.leaf.async.AsyncPlayerDataSaving.submit(() -> {
103+ + var nbtBytes = new it.unimi.dsi.fastutil.io.FastByteArrayOutputStream(65536);
104+ + NbtIo.writeCompressed(tag, nbtBytes);
105+ Path path1 = Files.createTempFile(path, "level", ".dat");
22106- NbtIo.writeCompressed(tag, path1);
23107- Path path2 = this.levelDirectory.oldDataFile();
24108- Path path3 = this.levelDirectory.dataFile();
25- - Util.safeReplaceFile(path3, path1, path2 );
26- + NbtIo.writeCompressed(tag, nbtBytes );
27- } catch (Exception var6) {
109+ + org.apache.commons.io.FileUtils.writeByteArrayToFile(path1.toFile(), nbtBytes.array, 0, nbtBytes.length, false );
110+ Util.safeReplaceFile(path3, path1, path2 );
111+ - } catch (Exception var6) {
28112- LevelStorageSource.LOGGER.error("Failed to save level {}", path, var6);
29- + LevelStorageSource.LOGGER.error("Failed to encode level {}", path, var6);
30- }
31- + org.dreeam.leaf.async.AsyncPlayerDataSaving.submit(() -> {
32- + try {
33- + Path path1 = Files.createTempFile(path, "level", ".dat");
34- + org.apache.commons.io.FileUtils.writeByteArrayToFile(path1.toFile(), nbtBytes.array, 0, nbtBytes.length, false);
35- + Path path2 = this.levelDirectory.oldDataFile();
36- + Path path3 = this.levelDirectory.dataFile();
37- + Util.safeReplaceFile(path3, path1, path2);
38- + } catch (Exception var6) {
39- + LevelStorageSource.LOGGER.error("Failed to save level {}", path, var6);
40- + }
41- + });
113+ - }
114+ + return null;
115+ + }, path3, org.dreeam.leaf.config.modules.async.AsyncPlayerDataSave.levelData);
42116+ // Leaf end - Async playerdata saving
43117 }
44118
45119 public Optional<Path> getIconFile() {
46120diff --git a/net/minecraft/world/level/storage/PlayerDataStorage.java b/net/minecraft/world/level/storage/PlayerDataStorage.java
47- index b8ef50bc3d07890c9da2c98d5f009a3adc52f4b0..4099f4ebf76c6bf74384aa5b9f5e889d53ebcfa9 100644
121+ index b8ef50bc3d07890c9da2c98d5f009a3adc52f4b0..11055fa067933083af5fca1c9d9e45435684f23b 100644
48122--- a/net/minecraft/world/level/storage/PlayerDataStorage.java
49123+++ b/net/minecraft/world/level/storage/PlayerDataStorage.java
50- @@ -23,6 +23,7 @@ public class PlayerDataStorage {
51- private static final Logger LOGGER = LogUtils.getLogger();
52- private final File playerDir;
53- protected final DataFixer fixerUpper;
54- + private final java.util.Map<java.util.UUID, java.util.concurrent.Future<?>> savingLocks = new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>(); // Leaf - Async playerdata saving
55-
56- public PlayerDataStorage(LevelStorageSource.LevelStorageAccess levelStorageAccess, DataFixer fixerUpper) {
57- this.fixerUpper = fixerUpper;
58- @@ -32,21 +33,84 @@ public class PlayerDataStorage {
59-
60- public void save(Player player) {
61- if (org.spigotmc.SpigotConfig.disablePlayerDataSaving) return; // Spigot
62- + // Leaf start - Async playerdata saving
63- + CompoundTag compoundTag;
64- try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(player.problemPath(), LOGGER)) {
65- TagValueOutput tagValueOutput = TagValueOutput.createWithContext(scopedCollector, player.registryAccess());
66- player.saveWithoutId(tagValueOutput);
67- - Path path = this.playerDir.toPath();
68- - Path path1 = Files.createTempFile(path, player.getStringUUID() + "-", ".dat");
69- - CompoundTag compoundTag = tagValueOutput.buildResult();
124+ @@ -38,15 +38,28 @@ public class PlayerDataStorage {
125+ Path path = this.playerDir.toPath();
126+ Path path1 = Files.createTempFile(path, player.getStringUUID() + "-", ".dat");
127+ CompoundTag compoundTag = tagValueOutput.buildResult();
70128- NbtIo.writeCompressed(compoundTag, path1);
71129- Path path2 = path.resolve(player.getStringUUID() + ".dat");
72130- Path path3 = path.resolve(player.getStringUUID() + ".dat_old");
73131- Util.safeReplaceFile(path2, path1, path3);
74- - } catch (Exception var11) {
75- - LOGGER.warn("Failed to save player data for {}", player.getPlainTextName(), var11); // Paper - Print exception
76- + compoundTag = tagValueOutput.buildResult();
77- + } catch (Exception exception) {
78- + LOGGER.warn("Failed to encode player data for {}", player.getPlainTextName(), exception);
79- + return;
132+ + save(player.getStringUUID(), compoundTag);
133+ } catch (Exception var11) {
134+ LOGGER.warn("Failed to save player data for {}", player.getPlainTextName(), var11); // Paper - Print exception
80135 }
81- + save(player.getScoreboardName(), player.getUUID(), player.getStringUUID(), compoundTag);
82- + // Leaf end - Async playerdata saving
83136 }
84137
85138+ // Leaf start - Async playerdata saving
86- + public void save(String playerName, java.util.UUID uniqueId, String stringId, CompoundTag compoundTag) {
87- + var nbtBytes = new it.unimi.dsi.fastutil.io.FastByteArrayOutputStream(65536);
88- + try {
139+ + public void save(String stringId, CompoundTag compoundTag) {
140+ + Path path = this.playerDir.toPath();
141+ + Path path2 = path.resolve(stringId + ".dat");
142+ + org.dreeam.leaf.async.AsyncPlayerDataSaving.submit(() -> {
143+ + var nbtBytes = new it.unimi.dsi.fastutil.io.FastByteArrayOutputStream(65536);
89144+ NbtIo.writeCompressed(compoundTag, nbtBytes);
90- + } catch (Exception exception) {
91- + LOGGER.warn("Failed to encode player data for {}", stringId, exception);
92- + }
93- + lockFor(uniqueId, playerName);
94- + synchronized (PlayerDataStorage.this) {
95- + org.dreeam.leaf.async.AsyncPlayerDataSaving.submit(() -> {
96- + try {
97- + Path path = this.playerDir.toPath();
98- + Path path1 = Files.createTempFile(path, stringId + "-", ".dat");
99- + org.apache.commons.io.FileUtils.writeByteArrayToFile(path1.toFile(), nbtBytes.array, 0, nbtBytes.length, false);
100- + Path path2 = path.resolve(stringId + ".dat");
101- + Path path3 = path.resolve(stringId + ".dat_old");
102- + Util.safeReplaceFile(path2, path1, path3);
103- + } catch (Exception var7) {
104- + LOGGER.warn("Failed to save player data for {}", playerName, var7);
105- + } finally {
106- + synchronized (PlayerDataStorage.this) {
107- + savingLocks.remove(uniqueId);
108- + }
109- + }
110- + }).ifPresent(future -> savingLocks.put(uniqueId, future));
111- + }
112- + }
113- +
114- + private void lockFor(java.util.UUID uniqueId, String playerName) {
115- + java.util.concurrent.Future<?> fut;
116- + synchronized (this) {
117- + fut = savingLocks.get(uniqueId);
118- + }
119- + if (fut == null) {
120- + return;
121- + }
122- + while (true) {
123- + try {
124- + fut.get(10_000L, java.util.concurrent.TimeUnit.MILLISECONDS);
125- + break;
126- + } catch (InterruptedException ignored) {
127- + } catch (java.util.concurrent.ExecutionException
128- + | java.util.concurrent.TimeoutException exception) {
129- + LOGGER.warn("Failed to save player data for {}", playerName, exception);
130- +
131- + String threadDump = "";
132- + var threadMXBean = java.lang.management.ManagementFactory.getThreadMXBean();
133- + for (var threadInfo : threadMXBean.dumpAllThreads(true, true)) {
134- + if (threadInfo.getThreadName().equals("Leaf IO Thread")) { // TODO: We should use instanceOf here
135- + threadDump = threadInfo.toString();
136- + break;
137- + }
138- + }
139- + LOGGER.warn(threadDump);
140- + fut.cancel(true);
141- + break;
142- + } finally {
143- + savingLocks.remove(uniqueId);
144- + }
145- + }
145+ + Path path1 = org.dreeam.leaf.async.AsyncPlayerDataSaving.tempFile(path, stringId, ".dat");
146+ + org.apache.commons.io.FileUtils.writeByteArrayToFile(path1.toFile(), nbtBytes.array, 0, nbtBytes.length, false);
147+ + Path path3 = path.resolve(stringId + ".dat_old");
148+ + Util.safeReplaceFile(path2, path1, path3);
149+ + return null;
150+ + }, path2, org.dreeam.leaf.config.modules.async.AsyncPlayerDataSave.playerdata);
146151+ }
147152+ // Leaf end - Async playerdata saving
148153+
149154 private void backup(NameAndId nameAndId, String suffix) {
150155 Path path = this.playerDir.toPath();
151156 String string = nameAndId.id().toString();
152- @@ -62 ,6 +126 ,7 @@ public class PlayerDataStorage {
153- }
154-
155- private Optional<CompoundTag> load(NameAndId nameAndId, String suffix) {
156- + lockFor(nameAndId.id (), nameAndId.name() ); // Leaf - Async playerdata saving
157- File file = new File(this.playerDir, nameAndId.id() + suffix );
158- // Spigot start
159- boolean usingWrongFile = false ;
157+ @@ -76 ,6 +89 ,7 @@ public class PlayerDataStorage {
158+ if (file.exists() && file.isFile()) {
159+ try {
160+ // Spigot start
161+ + org.dreeam.leaf.async.AsyncPlayerDataSaving.submit(file.toPath (), org.dreeam.leaf.config.modules.async.AsyncPlayerDataSave.playerdata ); // Leaf - Async playerdata saving
162+ Optional<CompoundTag> optional = Optional.of(NbtIo.readCompressed(file.toPath(), NbtAccounter.unlimitedHeap()) );
163+ if (usingWrongFile) {
164+ file.renameTo(new File(file.getPath() + ".offline-read")) ;
0 commit comments