From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: tr7zw <tr7zw@live.de>
Date: Fri, 31 Jul 2020 22:39:45 -0500
Subject: [PATCH] Player-saving-async-FileIO


diff --git a/src/main/java/net/minecraft/server/EntityHuman.java b/src/main/java/net/minecraft/server/EntityHuman.java
index ce4ebc96c01f3dacf4e4d0569d86f52140440d43..a52c0391b171c8a57de75f87c534ce1e0e78c44a 100644
--- a/src/main/java/net/minecraft/server/EntityHuman.java
+++ b/src/main/java/net/minecraft/server/EntityHuman.java
@@ -700,11 +700,20 @@ public abstract class EntityHuman extends EntityLiving {
 
     }
 
+    // Yatopia start
+    private NBTTagList inventorySnapshot = null;
+    private NBTTagList enderchestSnapshot = null;
+    public void takeSnapshot() {
+        inventorySnapshot = this.inventory.a(new NBTTagList());
+        enderchestSnapshot = this.enderChest.g();
+    }
+    // Yatopia end
+
     @Override
     public void saveData(NBTTagCompound nbttagcompound) {
         super.saveData(nbttagcompound);
         nbttagcompound.setInt("DataVersion", SharedConstants.getGameVersion().getWorldVersion());
-        nbttagcompound.set("Inventory", this.inventory.a(new NBTTagList()));
+        nbttagcompound.set("Inventory", inventorySnapshot != null ? inventorySnapshot : this.inventory.a(new NBTTagList())); inventorySnapshot = null; // Yatopia
         nbttagcompound.setInt("SelectedItemSlot", this.inventory.itemInHandIndex);
         nbttagcompound.setShort("SleepTimer", (short) this.sleepTicks);
         nbttagcompound.setFloat("XpP", this.exp);
@@ -714,7 +723,7 @@ public abstract class EntityHuman extends EntityLiving {
         nbttagcompound.setInt("Score", this.getScore());
         this.foodData.b(nbttagcompound);
         this.abilities.a(nbttagcompound);
-        nbttagcompound.set("EnderItems", this.enderChest.g());
+        nbttagcompound.set("EnderItems", enderchestSnapshot != null ? enderchestSnapshot : this.enderChest.g()); enderchestSnapshot = null; // Yatopia
         if (!this.getShoulderEntityLeft().isEmpty()) {
             nbttagcompound.set("ShoulderEntityLeft", this.getShoulderEntityLeft());
         }
diff --git a/src/main/java/net/minecraft/server/PlayerList.java b/src/main/java/net/minecraft/server/PlayerList.java
index 5760b40bdbee417f5bb5210f1271241ff8f36350..437698cf4fee266c87eebb8b5f05509b587913e7 100644
--- a/src/main/java/net/minecraft/server/PlayerList.java
+++ b/src/main/java/net/minecraft/server/PlayerList.java
@@ -1286,6 +1286,18 @@ public abstract class PlayerList {
             if (team != null) scoreboard.removeTeam(team);
         }
         // Paper end
+
+        // Yatopia start - make sure all saves are done
+        try {
+            playerFileData.saveThread.shutdown();
+            boolean done = playerFileData.saveThread.awaitTermination(30, java.util.concurrent.TimeUnit.SECONDS);
+            if (!done) {
+                LOGGER.error("Players did not save completely!");
+            }
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+        // Yatopia end
     }
     // Paper end
 
@@ -1323,13 +1335,13 @@ public abstract class PlayerList {
             File file = this.server.a(SavedFile.STATS).toFile();
             File file1 = new File(file, uuid + ".json");
 
-            if (!file1.exists()) {
+            /*if (!file1.exists()) { // Yatopia - don't check for old stats files with sync file IO
                 File file2 = new File(file, displayName + ".json"); // CraftBukkit
 
                 if (file2.exists() && file2.isFile()) {
                     file2.renameTo(file1);
                 }
-            }
+            }*/ // Yatopia
 
             serverstatisticmanager = new ServerStatisticManager(this.server, file1);
             // this.o.put(uuid, serverstatisticmanager); // CraftBukkit
diff --git a/src/main/java/net/minecraft/server/WorldNBTStorage.java b/src/main/java/net/minecraft/server/WorldNBTStorage.java
index a959672f5857b987001252c3fd7ace9e83e07c9b..d8969f7f5244548452e5d12325e6fa735ecd3a00 100644
--- a/src/main/java/net/minecraft/server/WorldNBTStorage.java
+++ b/src/main/java/net/minecraft/server/WorldNBTStorage.java
@@ -17,6 +17,7 @@ public class WorldNBTStorage {
     private static final Logger LOGGER = LogManager.getLogger();
     private final File playerDir;
     protected final DataFixer a;
+    public java.util.concurrent.ExecutorService saveThread = java.util.concurrent.Executors.newSingleThreadExecutor(); // Yatopia
 
     public WorldNBTStorage(Convertable.ConversionSession convertable_conversionsession, DataFixer datafixer) {
         this.a = datafixer;
@@ -26,6 +27,8 @@ public class WorldNBTStorage {
 
     public void save(EntityHuman entityhuman) {
         if(!com.destroystokyo.paper.PaperConfig.savePlayerData) return; // Paper - Make player data saving configurable
+        entityhuman.takeSnapshot(); // Yatopia
+        saveThread.execute(() -> { // Yatopia
         try {
             NBTTagCompound nbttagcompound = entityhuman.save(new NBTTagCompound());
             File file = File.createTempFile(entityhuman.getUniqueIDString() + "-", ".dat", this.playerDir);
@@ -38,7 +41,7 @@ public class WorldNBTStorage {
         } catch (Exception exception) {
             WorldNBTStorage.LOGGER.error("Failed to save player data for {}", entityhuman.getName(), exception); // Paper
         }
-
+        }); // Yatopia
     }
 
     @Nullable
