From a0794eaab90efbc2199e86c59851b99e46ea859e Mon Sep 17 00:00:00 2001 From: DoggySazHi Date: Mon, 24 Jun 2024 21:47:56 -0700 Subject: [PATCH] Implement asynchronous NBT saving --- .../nitori/core/MixinCraftPlayer.java | 32 +++++++++++++++++++ .../nitori/core/MixinLevelStorageAccess.java | 32 +++++++++++++++++++ .../nitori/core/MixinPlayerList.java | 28 ++++++++++++++++ src/main/resources/mixins.core.json | 5 ++- 4 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 src/main/java/net/gensokyoreimagined/nitori/core/MixinCraftPlayer.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/core/MixinLevelStorageAccess.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/core/MixinPlayerList.java diff --git a/src/main/java/net/gensokyoreimagined/nitori/core/MixinCraftPlayer.java b/src/main/java/net/gensokyoreimagined/nitori/core/MixinCraftPlayer.java new file mode 100644 index 0000000..4a4f8b1 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/core/MixinCraftPlayer.java @@ -0,0 +1,32 @@ +package net.gensokyoreimagined.nitori.core; + +import net.minecraft.Util; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.storage.PlayerDataStorage; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.util.concurrent.CompletableFuture; + +@Mixin(CraftPlayer.class) +public class MixinCraftPlayer { + @Redirect(method = "saveData", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/storage/PlayerDataStorage;save(Lnet/minecraft/world/entity/player/Player;)V")) + private void gensouHacks$savePlayerData(PlayerDataStorage instance, Player path) { + Runnable writeRunnable = () -> { + var craftPlayer = (CraftPlayer) (Object) this; + var server = craftPlayer.getServer(); + + if (server instanceof CraftServer craftServer) { + var handle = craftPlayer.getHandle(); + var playerIo = craftServer.getHandle().playerIo; + playerIo.save(handle); + } + }; + + var ioExecutor = Util.backgroundExecutor(); + CompletableFuture.runAsync(writeRunnable, ioExecutor); + } +} diff --git a/src/main/java/net/gensokyoreimagined/nitori/core/MixinLevelStorageAccess.java b/src/main/java/net/gensokyoreimagined/nitori/core/MixinLevelStorageAccess.java new file mode 100644 index 0000000..3ec8dae --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/core/MixinLevelStorageAccess.java @@ -0,0 +1,32 @@ +package net.gensokyoreimagined.nitori.core; + +import net.minecraft.Util; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.level.storage.LevelStorageSource; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.util.concurrent.CompletableFuture; + +@Mixin(LevelStorageSource.LevelStorageAccess.class) +public abstract class MixinLevelStorageAccess { + @Shadow protected abstract void saveLevelData(CompoundTag nbt); + + @Redirect(method = "modifyLevelDataWithoutDatafix", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/storage/LevelStorageSource$LevelStorageAccess;saveLevelData(Lnet/minecraft/nbt/CompoundTag;)V")) + private void gensouHacks$saveLevelData(LevelStorageSource.LevelStorageAccess levelStorageAccess, CompoundTag compoundTag) { + Runnable writeRunnable = () -> saveLevelData(compoundTag); + + var ioExecutor = Util.backgroundExecutor(); + CompletableFuture.runAsync(writeRunnable, ioExecutor); + } + + @Redirect(method = "saveDataTag(Lnet/minecraft/core/RegistryAccess;Lnet/minecraft/world/level/storage/WorldData;Lnet/minecraft/nbt/CompoundTag;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/storage/LevelStorageSource$LevelStorageAccess;saveLevelData(Lnet/minecraft/nbt/CompoundTag;)V")) + private void gensouHacks$saveLevelData2(LevelStorageSource.LevelStorageAccess levelStorageAccess, CompoundTag compoundTag) { + Runnable writeRunnable = () -> saveLevelData(compoundTag); + + var ioExecutor = Util.backgroundExecutor(); + CompletableFuture.runAsync(writeRunnable, ioExecutor); + } +} diff --git a/src/main/java/net/gensokyoreimagined/nitori/core/MixinPlayerList.java b/src/main/java/net/gensokyoreimagined/nitori/core/MixinPlayerList.java new file mode 100644 index 0000000..3e7758c --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/core/MixinPlayerList.java @@ -0,0 +1,28 @@ +package net.gensokyoreimagined.nitori.core; + +import net.minecraft.Util; +import net.minecraft.server.players.PlayerList; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.storage.PlayerDataStorage; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.util.concurrent.CompletableFuture; + +@Mixin(PlayerList.class) +public class MixinPlayerList { + @Final + @Shadow + public PlayerDataStorage playerIo; + + @Redirect(method = "save", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/storage/PlayerDataStorage;save(Lnet/minecraft/world/entity/player/Player;)V")) + private void gensouHacks$savePlayerData(PlayerDataStorage instance, Player player) { + Runnable writeRunnable = () -> playerIo.save(player); + + var ioExecutor = Util.backgroundExecutor(); + CompletableFuture.runAsync(writeRunnable, ioExecutor); + } +} diff --git a/src/main/resources/mixins.core.json b/src/main/resources/mixins.core.json index 0e7ef4a..df12eec 100755 --- a/src/main/resources/mixins.core.json +++ b/src/main/resources/mixins.core.json @@ -26,6 +26,9 @@ "MixinWorldGenRegion", "ChunkMapMixin", "ChunkMapMixin$TrackedEntity", - "MixinReobfServer" + "MixinReobfServer", + "MixinLevelStorageAccess", + "MixinPlayerList", + "MixinCraftPlayer" ] }