diff --git a/patches/server/1046-Async-StoredUserList-saving.patch b/patches/server/1046-Async-StoredUserList-saving.patch new file mode 100644 index 0000000000..a1bf9fcf47 --- /dev/null +++ b/patches/server/1046-Async-StoredUserList-saving.patch @@ -0,0 +1,200 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Tran Nguyen Ngoc Quang <3d7777456@gmail.com> +Date: Thu, 8 Feb 2024 04:44:10 +0700 +Subject: [PATCH] Async StoredUserList saving + + +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index d06185566b447c432d4dc2e3ba04d121bcdbc71b..4c460a5605b282b6ace7216128d1d6b231404d8e 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -979,6 +979,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop> { + private static final Gson GSON = (new GsonBuilder()).setPrettyPrinting().create(); + private final File file; + private final Map map = Maps.newConcurrentMap(); // Paper - Use ConcurrentHashMap in JsonList ++ // Paper start - async StoredUserList saving ++ private static final java.util.concurrent.ExecutorService SAVE_EXECUTOR = java.util.concurrent.Executors.newSingleThreadExecutor( ++ new com.google.common.util.concurrent.ThreadFactoryBuilder() ++ .setNameFormat("Async StoredUserList Saving - %1$d") ++ .build() ++ ); ++ // Paper end + + public StoredUserList(File file) { + this.file = file; +@@ -45,7 +52,7 @@ public abstract class StoredUserList> { + this.map.put(this.getKeyForUser(entry.getUser()), entry); + + try { +- this.save(); ++ this.save(true); // Paper - async StoredUserList saving + } catch (IOException ioexception) { + StoredUserList.LOGGER.warn("Could not save the list after adding a user.", ioexception); + } +@@ -65,7 +72,7 @@ public abstract class StoredUserList> { + this.map.remove(this.getKeyForUser(key)); + + try { +- this.save(); ++ this.save(true); // Paper - async StoredUserList saving + } catch (IOException ioexception) { + StoredUserList.LOGGER.warn("Could not save the list after removing a user.", ioexception); + } +@@ -102,8 +109,22 @@ public abstract class StoredUserList> { + return this.map.values(); + } + +- public void save() throws IOException { ++ // Paper start - async StoredUserList saving ++ static void shutdownExecutor() { ++ final java.util.concurrent.ExecutorService executor = StoredUserList.SAVE_EXECUTOR; ++ ++ executor.shutdown(); ++ try { ++ executor.awaitTermination(30L, java.util.concurrent.TimeUnit.SECONDS); ++ } catch (java.lang.InterruptedException ex) { ++ executor.shutdownNow(); ++ Thread.currentThread().interrupt(); ++ } ++ } ++ public void save(boolean async) throws IOException { ++ // Paper end + this.removeExpired(); // Paper - remove expired values before saving ++ Runnable save = () -> { // Paper - async StoredUserList saving + JsonArray jsonarray = new JsonArray(); + Stream stream = this.map.values().stream().map((jsonlistentry) -> { // CraftBukkit - decompile error + JsonObject jsonobject = new JsonObject(); +@@ -114,6 +135,7 @@ public abstract class StoredUserList> { + + Objects.requireNonNull(jsonarray); + stream.forEach(jsonarray::add); ++ try { // Paper - async StoredUserList saving + BufferedWriter bufferedwriter = Files.newWriter(this.file, StandardCharsets.UTF_8); + + try { +@@ -133,6 +155,17 @@ public abstract class StoredUserList> { + if (bufferedwriter != null) { + bufferedwriter.close(); + } ++ // Paper start - async StoredUserList saving ++ } catch (IOException ex) { ++ LOGGER.error("Something went wrong while saving StoredUserList {}", this.file.getName(), ex); ++ } ++ }; ++ if (async) { ++ StoredUserList.SAVE_EXECUTOR.execute(save); ++ } else { ++ save.run(); ++ } ++ // Paper end + + } +