From da9c2ac4cc825a3a567cd0d5ac547941cb6c1822 Mon Sep 17 00:00:00 2001 From: Daniel Walsh Date: Wed, 7 Feb 2024 09:26:09 +0000 Subject: [PATCH] Move PlayerProfile saving off the main thread (#4119) * Move PlayerProfile off main thread, add debugs and improve tab completion for debug Moved the PlayerProfile saving off the main thread, we generally load this off-thread but now we also save off-thread. I thought we were already doing this but apparently not, especially with our current YAML stuff this should definitely be done Also done a small change to ensure that we don't remove the PlayerProfile from memory if the player is still online. I don't think we ever had a reported issue from this but it's kinda weird behaviour Finally, added some debug logs to the saving logic, this can be enabled with `sf debug slimefun_player_profile_data`. Also added auto-complete to /sf debug because it's nice, this only works for Slimefun test cases rather than addons but that's fine. Mostly internal anyway * Update src/main/java/io/github/thebusybiscuit/slimefun4/api/player/PlayerProfile.java --------- Co-authored-by: Alessio Colombo <37039432+Sfiguz7@users.noreply.github.com> --- .../slimefun4/api/player/PlayerProfile.java | 5 ++++- .../core/commands/SlimefunTabCompleter.java | 8 +++++++ .../slimefun4/core/debug/TestCase.java | 12 ++++++++++- .../core/services/AutoSavingService.java | 21 ++++++++++++++++--- 4 files changed, 41 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/player/PlayerProfile.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/player/PlayerProfile.java index 96290f484..718547136 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/player/PlayerProfile.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/player/PlayerProfile.java @@ -8,7 +8,6 @@ import java.util.OptionalInt; import java.util.Set; import java.util.UUID; import java.util.function.Consumer; -import java.util.stream.IntStream; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -35,6 +34,8 @@ import io.github.thebusybiscuit.slimefun4.api.items.HashedArmorpiece; import io.github.thebusybiscuit.slimefun4.api.researches.Research; import io.github.thebusybiscuit.slimefun4.core.attributes.ProtectionType; import io.github.thebusybiscuit.slimefun4.core.attributes.ProtectiveArmor; +import io.github.thebusybiscuit.slimefun4.core.debug.Debug; +import io.github.thebusybiscuit.slimefun4.core.debug.TestCase; import io.github.thebusybiscuit.slimefun4.core.guide.GuideHistory; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import io.github.thebusybiscuit.slimefun4.implementation.items.armor.SlimefunArmorPiece; @@ -237,6 +238,7 @@ public class PlayerProfile { * The profile can then be removed from RAM. */ public final void markForDeletion() { + Debug.log(TestCase.PLAYER_PROFILE_DATA, "Marking {} ({}) profile for deletion", name, ownerId); markedForDeletion = true; } @@ -244,6 +246,7 @@ public class PlayerProfile { * Call this method if this Profile has unsaved changes. */ public final void markDirty() { + Debug.log(TestCase.PLAYER_PROFILE_DATA, "Marking {} ({}) profile as dirty", name, ownerId); dirty = true; } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/SlimefunTabCompleter.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/SlimefunTabCompleter.java index 07ca70bf0..50a665f30 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/SlimefunTabCompleter.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/SlimefunTabCompleter.java @@ -16,6 +16,7 @@ import org.bukkit.command.TabCompleter; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; import io.github.thebusybiscuit.slimefun4.api.researches.Research; +import io.github.thebusybiscuit.slimefun4.core.debug.TestCase; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; class SlimefunTabCompleter implements TabCompleter { @@ -33,6 +34,13 @@ class SlimefunTabCompleter implements TabCompleter { public List onTabComplete(CommandSender sender, Command cmd, String label, String[] args) { if (args.length == 1) { return createReturnList(command.getSubCommandNames(), args[0]); + } else if (args.length == 2) { + if (args[0].equalsIgnoreCase("debug")) { + return createReturnList(TestCase.VALUES_LIST, args[1]); + } else { + // Returning null will make it fallback to the default arguments (all online players) + return null; + } } else if (args.length == 3) { if (args[0].equalsIgnoreCase("give")) { return createReturnList(getSlimefunItems(), args[2]); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/debug/TestCase.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/debug/TestCase.java index 00b3bbf70..dec31592d 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/debug/TestCase.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/debug/TestCase.java @@ -1,5 +1,7 @@ package io.github.thebusybiscuit.slimefun4.core.debug; +import java.util.Arrays; +import java.util.List; import java.util.Locale; import javax.annotation.Nonnull; @@ -17,7 +19,15 @@ public enum TestCase { * being checked and why it is comparing IDs or meta. * This is helpful for us to check into why input nodes are taking a while for servers. */ - CARGO_INPUT_TESTING; + CARGO_INPUT_TESTING, + + /** + * Debug information regarding player profile loading, saving and handling. + * This is an area we're currently changing quite a bit and this will help ensure we're doing it safely + */ + PLAYER_PROFILE_DATA; + + public static final List VALUES_LIST = Arrays.stream(values()).map(TestCase::toString).toList(); TestCase() {} diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/AutoSavingService.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/AutoSavingService.java index a0455323f..060ce0d77 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/AutoSavingService.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/AutoSavingService.java @@ -13,6 +13,8 @@ import org.bukkit.block.Block; import org.bukkit.entity.Player; import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile; +import io.github.thebusybiscuit.slimefun4.core.debug.Debug; +import io.github.thebusybiscuit.slimefun4.core.debug.TestCase; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import me.mrCookieSlime.Slimefun.api.BlockStorage; @@ -39,9 +41,8 @@ public class AutoSavingService { public void start(@Nonnull Slimefun plugin, int interval) { this.interval = interval; - plugin.getServer().getScheduler().runTaskTimer(plugin, this::saveAllPlayers, 2000L, interval * 60L * 20L); + plugin.getServer().getScheduler().runTaskTimerAsynchronously(plugin, this::saveAllPlayers, 2000L, interval * 60L * 20L); plugin.getServer().getScheduler().runTaskTimerAsynchronously(plugin, this::saveAllBlocks, 2000L, interval * 60L * 20L); - } /** @@ -52,16 +53,30 @@ public class AutoSavingService { Iterator iterator = PlayerProfile.iterator(); int players = 0; + Debug.log(TestCase.PLAYER_PROFILE_DATA, "Saving all players data"); + while (iterator.hasNext()) { PlayerProfile profile = iterator.next(); if (profile.isDirty()) { players++; profile.save(); + + Debug.log(TestCase.PLAYER_PROFILE_DATA, "Saved data for {} ({})", + profile.getPlayer() != null ? profile.getPlayer().getName() : "Unknown", profile.getUUID() + ); } - if (profile.isMarkedForDeletion()) { + // Remove the PlayerProfile from memory if the player has left the server (marked from removal) + // and they're still not on the server + // At this point, we've already saved their profile so we can safely remove it + // without worry for having a data sync issue (e.g. data is changed but then we try to re-load older data) + if (profile.isMarkedForDeletion() && profile.getPlayer() == null) { iterator.remove(); + + Debug.log(TestCase.PLAYER_PROFILE_DATA, "Removed data from memory for {}", + profile.getUUID() + ); } }