mirror of
https://github.com/Winds-Studio/Leaf.git
synced 2026-01-04 15:41:40 +00:00
318 lines
17 KiB
Diff
318 lines
17 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com>
|
|
Date: Fri, 23 Aug 2024 22:04:20 -0400
|
|
Subject: [PATCH] Nitori: Async playerdata saving
|
|
|
|
Original license: GPL v3
|
|
Original project: https://github.com/Gensokyo-Reimagined/Nitori
|
|
|
|
diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java
|
|
index 00a82873d226f113278632a53c0faca420dd67d4..2c4423eb2d465c2782a8dab851619ce539f69ae8 100644
|
|
--- a/net/minecraft/network/Connection.java
|
|
+++ b/net/minecraft/network/Connection.java
|
|
@@ -586,7 +586,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
|
// Paper end - Optimize network
|
|
|
|
private static final int MAX_PER_TICK = io.papermc.paper.configuration.GlobalConfiguration.get().misc.maxJoinsPerTick; // Paper - Buffer joins to world
|
|
- private static int joinAttemptsThisTick; // Paper - Buffer joins to world
|
|
+ public static int joinAttemptsThisTick; // Paper - Buffer joins to world // Leaf - Async player IO
|
|
private static int currTick; // Paper - Buffer joins to world
|
|
private static int tickSecond; // Purpur - Max joins per second
|
|
public void tick() {
|
|
diff --git a/net/minecraft/server/PlayerAdvancements.java b/net/minecraft/server/PlayerAdvancements.java
|
|
index d2159a747fe42aa95cfc6bca0e55e3f4485847bb..d5196b181a0a633cb04ce18b0471cda2dcaa8816 100644
|
|
--- a/net/minecraft/server/PlayerAdvancements.java
|
|
+++ b/net/minecraft/server/PlayerAdvancements.java
|
|
@@ -111,6 +111,7 @@ public class PlayerAdvancements {
|
|
}
|
|
|
|
private void load(ServerAdvancementManager manager) {
|
|
+ org.dreeam.leaf.async.storage.AsyncPlayerDataSaving.INSTANCE.blockAdvancements(player.getUUID(), player.getScoreboardName()); // Leaf - Async player IO
|
|
if (Files.isRegularFile(this.playerSavePath)) {
|
|
try (JsonReader jsonReader = new JsonReader(Files.newBufferedReader(this.playerSavePath, StandardCharsets.UTF_8))) {
|
|
jsonReader.setLenient(false);
|
|
@@ -133,12 +134,18 @@ public class PlayerAdvancements {
|
|
JsonElement jsonElement = this.codec.encodeStart(JsonOps.INSTANCE, this.asData()).getOrThrow();
|
|
|
|
try {
|
|
- FileUtil.createDirectoriesSafe(this.playerSavePath.getParent());
|
|
-
|
|
- try (Writer bufferedWriter = Files.newBufferedWriter(this.playerSavePath, StandardCharsets.UTF_8)) {
|
|
- GSON.toJson(jsonElement, GSON.newJsonWriter(bufferedWriter));
|
|
- }
|
|
- } catch (JsonIOException | IOException var7) {
|
|
+ // Leaf start - Async player IO
|
|
+ String content = GSON.toJson(jsonElement);
|
|
+ final Path path = this.playerSavePath;
|
|
+ org.dreeam.leaf.async.storage.AsyncPlayerDataSaving.INSTANCE.submitAdvancements(
|
|
+ this.player.getUUID(),
|
|
+ this.player.getScoreboardName(),
|
|
+ () -> {
|
|
+ org.dreeam.leaf.async.storage.AsyncPlayerDataSaving.safeReplace(path, content);
|
|
+ return null;
|
|
+ });
|
|
+ } catch (JsonIOException /*| IOException*/ var7) {
|
|
+ // Leaf end - Async player IO
|
|
LOGGER.error("Couldn't save player advancements to {}", this.playerSavePath, var7);
|
|
}
|
|
}
|
|
diff --git a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
|
index 114b25f933c6a1b011523581a5a02a5a2c1e827e..3da6dad3dd0f4c5750609b382f47a6cd14f18e7a 100644
|
|
--- a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
|
+++ b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
|
@@ -79,6 +79,7 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener,
|
|
.expireAfterWrite(org.dreeam.leaf.config.modules.misc.Cache.cachePlayerProfileResultTimeout, java.util.concurrent.TimeUnit.MINUTES)
|
|
.build();
|
|
// Leaf end - Cache player profileResult
|
|
+ @Nullable public java.util.UUID[] duplicateDisconnect = null; // Leaf - Async player IO
|
|
|
|
public ServerLoginPacketListenerImpl(MinecraftServer server, Connection connection, boolean transferred) {
|
|
this.server = server;
|
|
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
|
|
index 75fb49f1596f475278d12c8c7aea9ad4952b6056..52a0fa425a30caa2e592c0fdda44800da169c2a0 100644
|
|
--- a/net/minecraft/server/players/PlayerList.java
|
|
+++ b/net/minecraft/server/players/PlayerList.java
|
|
@@ -782,6 +782,31 @@ public abstract class PlayerList {
|
|
// UserBanListEntry userBanListEntry = this.bans.get(gameProfile);
|
|
// Moved from processLogin
|
|
UUID uuid = gameProfile.getId();
|
|
+
|
|
+ // Leaf start - Async player IO
|
|
+ if (org.dreeam.leaf.config.modules.async.AsyncPlayerDataSave.enabled) {
|
|
+ if (org.dreeam.leaf.async.storage.AsyncPlayerDataSaving.INSTANCE.isSaving(uuid)) {
|
|
+ if (Connection.joinAttemptsThisTick != 0) Connection.joinAttemptsThisTick -= 1;
|
|
+ return null;
|
|
+ }
|
|
+ if (loginlistener.duplicateDisconnect != null
|
|
+ && loginlistener.duplicateDisconnect.length != 0) {
|
|
+ // check last one
|
|
+ var last = loginlistener.duplicateDisconnect[loginlistener.duplicateDisconnect.length - 1];
|
|
+ if (org.dreeam.leaf.async.storage.AsyncPlayerDataSaving.INSTANCE.isSaving(last)) {
|
|
+ if (Connection.joinAttemptsThisTick != 0) Connection.joinAttemptsThisTick -= 1;
|
|
+ return null;
|
|
+ }
|
|
+ for (UUID uuid1 : loginlistener.duplicateDisconnect) {
|
|
+ if (org.dreeam.leaf.async.storage.AsyncPlayerDataSaving.INSTANCE.isSaving(uuid1)) {
|
|
+ if (Connection.joinAttemptsThisTick != 0) Connection.joinAttemptsThisTick -= 1;
|
|
+ return null;
|
|
+ }
|
|
+ }
|
|
+ loginlistener.duplicateDisconnect = null;
|
|
+ }
|
|
+ }
|
|
+ // Leaf end - Async player IO
|
|
List<ServerPlayer> list = Lists.newArrayList();
|
|
|
|
ServerPlayer entityplayer;
|
|
@@ -793,6 +818,23 @@ public abstract class PlayerList {
|
|
}
|
|
}
|
|
|
|
+ // Leaf start - Async player IO
|
|
+ if (org.dreeam.leaf.config.modules.async.AsyncPlayerDataSave.enabled && !list.isEmpty()) {
|
|
+ loginlistener.duplicateDisconnect = new UUID[list.size()];
|
|
+ java.util.Iterator<ServerPlayer> iterator = list.iterator();
|
|
+
|
|
+ int index = 0;
|
|
+ while (iterator.hasNext()) {
|
|
+ entityplayer = iterator.next();
|
|
+ // this.save(entityplayer); // CraftBukkit - Force the player's inventory to be saved
|
|
+ entityplayer.connection.disconnect(Component.translatable("multiplayer.disconnect.duplicate_login"), org.bukkit.event.player.PlayerKickEvent.Cause.DUPLICATE_LOGIN); // Paper - kick event cause
|
|
+ loginlistener.duplicateDisconnect[index] = entityplayer.getUUID();
|
|
+ index++;
|
|
+ }
|
|
+ if (Connection.joinAttemptsThisTick != 0) Connection.joinAttemptsThisTick -= 1;
|
|
+ return null;
|
|
+ }
|
|
+ // Leaf end - Async player IO
|
|
java.util.Iterator iterator = list.iterator();
|
|
|
|
while (iterator.hasNext()) {
|
|
@@ -1582,7 +1624,7 @@ public abstract class PlayerList {
|
|
*/
|
|
// Leaf end - Remove useless creating stats json bases on player name logic
|
|
|
|
- serverStatsCounter = new ServerStatsCounter(this.server, file1);
|
|
+ serverStatsCounter = new ServerStatsCounter(this.server, file1, displayName, uuid);
|
|
// this.stats.put(uuid, serverStatsCounter); // CraftBukkit
|
|
}
|
|
|
|
diff --git a/net/minecraft/stats/ServerStatsCounter.java b/net/minecraft/stats/ServerStatsCounter.java
|
|
index b26dbe807e5cb0a42f6c06b933397902310e5616..a682ae890850a5e62acd54029f2a4eb1e69d200a 100644
|
|
--- a/net/minecraft/stats/ServerStatsCounter.java
|
|
+++ b/net/minecraft/stats/ServerStatsCounter.java
|
|
@@ -39,12 +39,31 @@ public class ServerStatsCounter extends StatsCounter {
|
|
private final File file;
|
|
private final Set<Stat<?>> dirty = Sets.newHashSet();
|
|
|
|
+ // Leaf start - Async player IO
|
|
+ private final String name;
|
|
+ private final java.util.UUID uuid;
|
|
+ @Deprecated(forRemoval = true)
|
|
public ServerStatsCounter(MinecraftServer server, File file) {
|
|
+ this(server, file, "UNKNOWN", parseUUID(file));
|
|
+ }
|
|
+ private static java.util.UUID parseUUID(File file) {
|
|
+ try {
|
|
+ return java.util.UUID.fromString(org.apache.commons.io.FilenameUtils.getBaseName(file.toString()));
|
|
+ } catch (IllegalArgumentException e) {
|
|
+ LOGGER.error("failed parse uuid {}", file, e);
|
|
+ return net.minecraft.Util.NIL_UUID;
|
|
+ }
|
|
+ }
|
|
+ public ServerStatsCounter(MinecraftServer server, File file, String name, java.util.UUID uuid) {
|
|
+ this.name = name;
|
|
+ this.uuid = uuid;
|
|
+ org.dreeam.leaf.async.storage.AsyncPlayerDataSaving.INSTANCE.blockStats(uuid, name);
|
|
+ // Leaf end - Async player IO
|
|
this.server = server;
|
|
this.file = file;
|
|
if (file.isFile()) {
|
|
try {
|
|
- this.parseLocal(server.getFixerUpper(), FileUtils.readFileToString(file));
|
|
+ this.parseLocal(server.getFixerUpper(), FileUtils.readFileToString(file, java.nio.charset.StandardCharsets.UTF_8)); // Leaf - UTF-8
|
|
} catch (IOException var4) {
|
|
LOGGER.error("Couldn't read statistics file {}", file, var4);
|
|
} catch (JsonParseException var5) {
|
|
@@ -66,11 +85,37 @@ public class ServerStatsCounter extends StatsCounter {
|
|
|
|
public void save() {
|
|
if (org.spigotmc.SpigotConfig.disableStatSaving) return; // Spigot
|
|
+ // Leaf start - Async player IO
|
|
+ Map<StatType<?>, JsonObject> map = Maps.newHashMap();
|
|
+ for (it.unimi.dsi.fastutil.objects.Object2IntMap.Entry<Stat<?>> entry : this.stats.object2IntEntrySet()) {
|
|
+ Stat<?> stat = entry.getKey();
|
|
+ map.computeIfAbsent(stat.getType(), type -> new JsonObject()).addProperty(getKey(stat).toString(), entry.getIntValue());
|
|
+ }
|
|
+ final File file = this.file;
|
|
+ org.dreeam.leaf.async.storage.AsyncPlayerDataSaving.INSTANCE.submitStats(
|
|
+ uuid,
|
|
+ name,
|
|
+ () -> {
|
|
+ JsonObject jsonObject = new JsonObject();
|
|
+
|
|
+ for (Entry<StatType<?>, JsonObject> entry1 : map.entrySet()) {
|
|
+ jsonObject.add(BuiltInRegistries.STAT_TYPE.getKey(entry1.getKey()).toString(), entry1.getValue());
|
|
+ }
|
|
+
|
|
+ JsonObject jsonObject1 = new JsonObject();
|
|
+ jsonObject1.add("stats", jsonObject);
|
|
+ jsonObject1.addProperty("DataVersion", SharedConstants.getCurrentVersion().getDataVersion().getVersion());
|
|
+ org.dreeam.leaf.async.storage.AsyncPlayerDataSaving.safeReplace(file.toPath(), jsonObject1.toString());
|
|
+ return null;
|
|
+ });
|
|
+ /*
|
|
try {
|
|
FileUtils.writeStringToFile(this.file, this.toJson());
|
|
} catch (IOException var2) {
|
|
LOGGER.error("Couldn't save stats", (Throwable)var2);
|
|
}
|
|
+ */
|
|
+ // Leaf end - Async player IO
|
|
}
|
|
|
|
@Override
|
|
diff --git a/net/minecraft/world/level/storage/PlayerDataStorage.java b/net/minecraft/world/level/storage/PlayerDataStorage.java
|
|
index c44110b123ba5912af18faf0065e9ded780da9b7..2eae5ccb37b942b94964c28391b96989ae85b072 100644
|
|
--- a/net/minecraft/world/level/storage/PlayerDataStorage.java
|
|
+++ b/net/minecraft/world/level/storage/PlayerDataStorage.java
|
|
@@ -34,19 +34,37 @@ public class PlayerDataStorage {
|
|
|
|
public void save(Player player) {
|
|
if (org.spigotmc.SpigotConfig.disablePlayerDataSaving) return; // Spigot
|
|
+ // Leaf start - Async player IO
|
|
+ CompoundTag compoundTag;
|
|
try {
|
|
- CompoundTag compoundTag = player.saveWithoutId(new CompoundTag());
|
|
- Path path = this.playerDir.toPath();
|
|
- Path path1 = Files.createTempFile(path, player.getStringUUID() + "-", ".dat");
|
|
- NbtIo.writeCompressed(compoundTag, path1);
|
|
- Path path2 = path.resolve(player.getStringUUID() + ".dat");
|
|
- Path path3 = path.resolve(player.getStringUUID() + ".dat_old");
|
|
- Util.safeReplaceFile(path2, path1, path3);
|
|
- } catch (Exception var7) {
|
|
- LOGGER.warn("Failed to save player data for {}", player.getScoreboardName(), var7); // Paper - Print exception
|
|
+ compoundTag = player.saveWithoutId(new CompoundTag());
|
|
+ } catch (Exception exception) {
|
|
+ LOGGER.warn("Failed to encode player data for {}", player.getScoreboardName(), exception);
|
|
+ return;
|
|
}
|
|
+ save(player.getScoreboardName(), player.getUUID(), player.getStringUUID(), compoundTag);
|
|
+ // Leaf end - Async player IO
|
|
}
|
|
|
|
+ // Leaf start - Async player IO
|
|
+ public void save(String playerName, java.util.UUID uuid, String stringId, CompoundTag compoundTag) {
|
|
+ final File playerDir = this.playerDir;
|
|
+ org.dreeam.leaf.async.storage.AsyncPlayerDataSaving.INSTANCE.submitEntity(
|
|
+ uuid,
|
|
+ playerName,
|
|
+ () -> {
|
|
+ var nbtBytes = new it.unimi.dsi.fastutil.io.FastByteArrayOutputStream(65536);
|
|
+ NbtIo.writeCompressed(compoundTag, nbtBytes);
|
|
+ Path path = playerDir.toPath();
|
|
+
|
|
+ Path current = path.resolve(stringId + ".dat");
|
|
+ Path old = path.resolve(stringId + ".dat_old");
|
|
+ org.dreeam.leaf.async.storage.AsyncPlayerDataSaving.safeReplaceBackup(current, old, nbtBytes.array, 0, nbtBytes.length);
|
|
+ return null;
|
|
+ });
|
|
+ }
|
|
+ // Leaf end - Async player IO
|
|
+
|
|
private void backup(String name, String stringUuid, String suffix) { // CraftBukkit
|
|
Path path = this.playerDir.toPath();
|
|
Path path1 = path.resolve(stringUuid + suffix); // CraftBukkit
|
|
@@ -60,7 +78,13 @@ public class PlayerDataStorage {
|
|
}
|
|
}
|
|
|
|
- private Optional<CompoundTag> load(String name, String stringUuid, String suffix) { // CraftBukkit
|
|
+ // Leaf start - Async player IO
|
|
+ private Optional<CompoundTag> load(String name, String stringUuid, String suffix) {
|
|
+ return load(name, stringUuid, suffix, java.util.UUID.fromString(stringUuid));
|
|
+ }
|
|
+ private Optional<CompoundTag> load(String name, String stringUuid, String suffix, java.util.UUID playerUuid) { // CraftBukkit
|
|
+ org.dreeam.leaf.async.storage.AsyncPlayerDataSaving.INSTANCE.blockEntity(playerUuid, name);
|
|
+ // Leaf end - Async player IO
|
|
File file = new File(this.playerDir, stringUuid + suffix); // CraftBukkit
|
|
// Spigot start
|
|
boolean usingWrongFile = false;
|
|
@@ -91,7 +115,7 @@ public class PlayerDataStorage {
|
|
|
|
public Optional<CompoundTag> load(Player player) {
|
|
// CraftBukkit start
|
|
- return this.load(player.getName().getString(), player.getStringUUID()).map((tag) -> {
|
|
+ return this.load(player.getName().getString(), player.getStringUUID(), player.getUUID()).map((tag) -> { // Leaf - Async player IO
|
|
if (player instanceof ServerPlayer serverPlayer) {
|
|
CraftPlayer craftPlayer = serverPlayer.getBukkitEntity();
|
|
// Only update first played if it is older than the one we have
|
|
@@ -106,20 +130,25 @@ public class PlayerDataStorage {
|
|
});
|
|
}
|
|
|
|
+ // Leaf start - Async player IO
|
|
public Optional<CompoundTag> load(String name, String uuid) {
|
|
+ return this.load(name, uuid, java.util.UUID.fromString(uuid));
|
|
+ }
|
|
+ public Optional<CompoundTag> load(String name, String uuid, java.util.UUID playerUuid) {
|
|
// CraftBukkit end
|
|
- Optional<CompoundTag> optional = this.load(name, uuid, ".dat"); // CraftBukkit
|
|
+ Optional<CompoundTag> optional = this.load(name, uuid, ".dat", playerUuid); // CraftBukkit
|
|
if (optional.isEmpty()) {
|
|
this.backup(name, uuid, ".dat"); // CraftBukkit
|
|
}
|
|
|
|
- return optional.or(() -> this.load(name, uuid, ".dat_old")).map(compoundTag -> { // CraftBukkit
|
|
+ return optional.or(() -> this.load(name, uuid, ".dat_old", playerUuid)).map(compoundTag -> { // CraftBukkit
|
|
int dataVersion = NbtUtils.getDataVersion(compoundTag, -1);
|
|
compoundTag = ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.PLAYER, compoundTag, dataVersion, net.minecraft.SharedConstants.getCurrentVersion().getDataVersion().getVersion()); // Paper - rewrite data conversion system
|
|
// player.load(compoundTag); // CraftBukkit - handled above
|
|
return compoundTag;
|
|
});
|
|
}
|
|
+ // Leaf end - Async player IO
|
|
|
|
// CraftBukkit start
|
|
public File getPlayerDir() {
|