From 7073c23bbbf1aac875d9e64dc8899455f8baa0c8 Mon Sep 17 00:00:00 2001 From: Hugo Planque <12386279+HookWoods@users.noreply.github.com> Date: Mon, 25 Jan 2021 17:37:16 +0100 Subject: [PATCH] New async nbt cache (#347) --- PATCHES.md | 1 + patches/server/0068-New-nbt-cache.patch | 129 ++++++++++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 patches/server/0068-New-nbt-cache.patch diff --git a/PATCHES.md b/PATCHES.md index 403a15c9..d96d7164 100644 --- a/PATCHES.md +++ b/PATCHES.md @@ -242,6 +242,7 @@ # Patches | server | Multi-Threaded ticking CraftBukkit | Spottedleaf | | | server | Name craft scheduler threads according to the plugin using | Spottedleaf | | | server | New Network System | Hugo Planque | Ivan Pekov | +| server | New nbt cache | Hugo Planque | | | server | Nuke streams off BlockPosition | Ivan Pekov | | | server | Nuke streams off SectionPosition | Ivan Pekov | | | server | Optimise EntityInsentient#checkDespawn | Spottedleaf | | diff --git a/patches/server/0068-New-nbt-cache.patch b/patches/server/0068-New-nbt-cache.patch new file mode 100644 index 00000000..e3bb4014 --- /dev/null +++ b/patches/server/0068-New-nbt-cache.patch @@ -0,0 +1,129 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Hugo Planque +Date: Thu, 21 Jan 2021 17:56:03 +0100 +Subject: [PATCH] New nbt cache + +The goal of this patch is to reduce I/O operations from the main thread while saving player data and also to avoid too many I/O operations while reading NBT Player file by using a cache (Which start to delete the oldest data when there is too much player compared to the map size) + +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index a1c2bea7c93433434b4e4dfd0bb4b9620657c40d..c7f1bf9d7fd86a1afbc1e4bb56dc56cef367283e 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -824,7 +824,9 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant dataCache = new org.yatopiamc.yatopia.server.cache.NBTCache<>(); // Yatopia - NBT Cache system ++ public final java.util.concurrent.ExecutorService executorService = java.util.concurrent.Executors.newSingleThreadExecutor(); // Yatopia - NBT Cache system + + public WorldNBTStorage(Convertable.ConversionSession convertable_conversionsession, DataFixer datafixer) { + this.a = datafixer; +@@ -30,11 +32,22 @@ public class WorldNBTStorage { + NBTTagCompound nbttagcompound = entityhuman.save(new NBTTagCompound()); + File file = File.createTempFile(entityhuman.getUniqueIDString() + "-", ".dat", this.playerDir); + +- NBTCompressedStreamTools.a(nbttagcompound, file); ++ // NBTCompressedStreamTools.a(nbttagcompound, file); // Yatopia ++ // Yatopia start - NBT Cache system ++ Runnable task = () -> { try { NBTCompressedStreamTools.a(nbttagcompound, file); + File file1 = new File(this.playerDir, entityhuman.getUniqueIDString() + ".dat"); + File file2 = new File(this.playerDir, entityhuman.getUniqueIDString() + ".dat_old"); + + SystemUtils.a(file1, file, file2); ++ } catch (Exception exception) { ++ WorldNBTStorage.LOGGER.error("Failed to save player data for {}", entityhuman.getName(), exception); // Paper ++ } ++ }; ++ synchronized (this.dataCache){ ++ this.dataCache.put(file, nbttagcompound); ++ } ++ this.executorService.execute(task); ++ // Yatopia end + } catch (Exception exception) { + WorldNBTStorage.LOGGER.error("Failed to save player data for {}", entityhuman.getName(), exception); // Paper + } +@@ -50,9 +63,18 @@ public class WorldNBTStorage { + // Spigot Start + boolean usingWrongFile = false; + boolean normalFile = file.exists() && file.isFile(); // Akarin - ensures normal file +- if ( org.bukkit.Bukkit.getOnlineMode() && !normalFile ) // Paper - Check online mode first // Akarin - ensures normal file ++ // if ( org.bukkit.Bukkit.getOnlineMode() && !normalFile ) // Paper - Check online mode first // Akarin - ensures normal file // Yatopia ++ // Yatopia start - NBT Cache system ++ NBTTagCompound playerData; ++ synchronized (this.dataCache){ ++ playerData = this.dataCache.get(file); ++ } ++ if (playerData == null && org.bukkit.Bukkit.getOnlineMode() && !normalFile ) // Paper - Check online mode first // Akarin - ensures normal file + { + file = new File( this.playerDir, java.util.UUID.nameUUIDFromBytes( ( "OfflinePlayer:" + entityhuman.getName() ).getBytes( "UTF-8" ) ).toString() + ".dat"); ++ synchronized (this.dataCache){ ++ playerData = this.dataCache.get(file); ++ } + if ( file.exists() ) + { + usingWrongFile = true; +@@ -60,10 +82,13 @@ public class WorldNBTStorage { + } + } + // Spigot End +- +- if (normalFile) { // Akarin - avoid double I/O operation ++ // if (normalFile) { // Akarin - avoid double I/O operation // Yatopia ++ if (playerData != null) { ++ nbttagcompound = playerData; ++ } else if (normalFile) { // Akarin - avoid double I/O operation + nbttagcompound = NBTCompressedStreamTools.a(file); + } ++ // Yatopia end + // Spigot Start + if ( usingWrongFile ) + { +diff --git a/src/main/java/org/yatopiamc/yatopia/server/cache/NBTCache.java b/src/main/java/org/yatopiamc/yatopia/server/cache/NBTCache.java +new file mode 100644 +index 0000000000000000000000000000000000000000..adc5975ded5f655a7b5da59daa1ff3b63697f841 +--- /dev/null ++++ b/src/main/java/org/yatopiamc/yatopia/server/cache/NBTCache.java +@@ -0,0 +1,29 @@ ++package org.yatopiamc.yatopia.server.cache; ++ ++import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenCustomHashMap; ++import net.minecraft.server.MinecraftServer; ++ ++public class NBTCache extends Object2ObjectLinkedOpenCustomHashMap { ++ ++ public NBTCache() { ++ super(100, 0.75F, new Strategy() { ++ @Override ++ public int hashCode(K k) { ++ return k.hashCode(); ++ } ++ ++ @Override ++ public boolean equals(K k, K k1) { ++ return k.equals(k1); ++ } ++ }); ++ } ++ ++ @Override ++ public V put(K k, V v) { ++ if (this.size() > MinecraftServer.getServer().getPlayerCount()) { ++ this.removeLast(); ++ } ++ return super.putAndMoveToFirst(k, v); ++ } ++}