mirror of
https://github.com/StarWishsama/Slimefun4.git
synced 2024-09-19 19:25:48 +00:00
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>
This commit is contained in:
parent
cd3672c3f2
commit
da9c2ac4cc
@ -8,7 +8,6 @@ import java.util.OptionalInt;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.stream.IntStream;
|
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
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.api.researches.Research;
|
||||||
import io.github.thebusybiscuit.slimefun4.core.attributes.ProtectionType;
|
import io.github.thebusybiscuit.slimefun4.core.attributes.ProtectionType;
|
||||||
import io.github.thebusybiscuit.slimefun4.core.attributes.ProtectiveArmor;
|
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.core.guide.GuideHistory;
|
||||||
import io.github.thebusybiscuit.slimefun4.implementation.Slimefun;
|
import io.github.thebusybiscuit.slimefun4.implementation.Slimefun;
|
||||||
import io.github.thebusybiscuit.slimefun4.implementation.items.armor.SlimefunArmorPiece;
|
import io.github.thebusybiscuit.slimefun4.implementation.items.armor.SlimefunArmorPiece;
|
||||||
@ -237,6 +238,7 @@ public class PlayerProfile {
|
|||||||
* The profile can then be removed from RAM.
|
* The profile can then be removed from RAM.
|
||||||
*/
|
*/
|
||||||
public final void markForDeletion() {
|
public final void markForDeletion() {
|
||||||
|
Debug.log(TestCase.PLAYER_PROFILE_DATA, "Marking {} ({}) profile for deletion", name, ownerId);
|
||||||
markedForDeletion = true;
|
markedForDeletion = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -244,6 +246,7 @@ public class PlayerProfile {
|
|||||||
* Call this method if this Profile has unsaved changes.
|
* Call this method if this Profile has unsaved changes.
|
||||||
*/
|
*/
|
||||||
public final void markDirty() {
|
public final void markDirty() {
|
||||||
|
Debug.log(TestCase.PLAYER_PROFILE_DATA, "Marking {} ({}) profile as dirty", name, ownerId);
|
||||||
dirty = true;
|
dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ import org.bukkit.command.TabCompleter;
|
|||||||
|
|
||||||
import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem;
|
import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem;
|
||||||
import io.github.thebusybiscuit.slimefun4.api.researches.Research;
|
import io.github.thebusybiscuit.slimefun4.api.researches.Research;
|
||||||
|
import io.github.thebusybiscuit.slimefun4.core.debug.TestCase;
|
||||||
import io.github.thebusybiscuit.slimefun4.implementation.Slimefun;
|
import io.github.thebusybiscuit.slimefun4.implementation.Slimefun;
|
||||||
|
|
||||||
class SlimefunTabCompleter implements TabCompleter {
|
class SlimefunTabCompleter implements TabCompleter {
|
||||||
@ -33,6 +34,13 @@ class SlimefunTabCompleter implements TabCompleter {
|
|||||||
public List<String> onTabComplete(CommandSender sender, Command cmd, String label, String[] args) {
|
public List<String> onTabComplete(CommandSender sender, Command cmd, String label, String[] args) {
|
||||||
if (args.length == 1) {
|
if (args.length == 1) {
|
||||||
return createReturnList(command.getSubCommandNames(), args[0]);
|
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) {
|
} else if (args.length == 3) {
|
||||||
if (args[0].equalsIgnoreCase("give")) {
|
if (args[0].equalsIgnoreCase("give")) {
|
||||||
return createReturnList(getSlimefunItems(), args[2]);
|
return createReturnList(getSlimefunItems(), args[2]);
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package io.github.thebusybiscuit.slimefun4.core.debug;
|
package io.github.thebusybiscuit.slimefun4.core.debug;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
@ -17,7 +19,15 @@ public enum TestCase {
|
|||||||
* being checked and why it is comparing IDs or meta.
|
* 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.
|
* 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<String> VALUES_LIST = Arrays.stream(values()).map(TestCase::toString).toList();
|
||||||
|
|
||||||
TestCase() {}
|
TestCase() {}
|
||||||
|
|
||||||
|
@ -13,6 +13,8 @@ import org.bukkit.block.Block;
|
|||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile;
|
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 io.github.thebusybiscuit.slimefun4.implementation.Slimefun;
|
||||||
|
|
||||||
import me.mrCookieSlime.Slimefun.api.BlockStorage;
|
import me.mrCookieSlime.Slimefun.api.BlockStorage;
|
||||||
@ -39,9 +41,8 @@ public class AutoSavingService {
|
|||||||
public void start(@Nonnull Slimefun plugin, int interval) {
|
public void start(@Nonnull Slimefun plugin, int interval) {
|
||||||
this.interval = 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);
|
plugin.getServer().getScheduler().runTaskTimerAsynchronously(plugin, this::saveAllBlocks, 2000L, interval * 60L * 20L);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -52,16 +53,30 @@ public class AutoSavingService {
|
|||||||
Iterator<PlayerProfile> iterator = PlayerProfile.iterator();
|
Iterator<PlayerProfile> iterator = PlayerProfile.iterator();
|
||||||
int players = 0;
|
int players = 0;
|
||||||
|
|
||||||
|
Debug.log(TestCase.PLAYER_PROFILE_DATA, "Saving all players data");
|
||||||
|
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
PlayerProfile profile = iterator.next();
|
PlayerProfile profile = iterator.next();
|
||||||
|
|
||||||
if (profile.isDirty()) {
|
if (profile.isDirty()) {
|
||||||
players++;
|
players++;
|
||||||
profile.save();
|
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();
|
iterator.remove();
|
||||||
|
|
||||||
|
Debug.log(TestCase.PLAYER_PROFILE_DATA, "Removed data from memory for {}",
|
||||||
|
profile.getUUID()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user