Sentropic / SkillAPI-s

A flexible API for easily creating skills and classes with visual skill trees
13 stars 10 forks source link

Auto saving mysql lost data. #14

Open chocolatecraft opened 3 years ago

chocolatecraft commented 3 years ago

Auto saving mysql lost data.

When I enabled this function some player got lost all data.

I find many time for check this problem. I got know it right now.

When I enabled config delay load data for sync multi server. its will lost data from auto save. I set delay "100" From config 100 = 5 sec Example.

When I join to server and then config will check delay for load sync data from mysql. And While delay loading... skillapi saving In the moment. it causing data loss.

if skillapi autosave when player join in 5 sec that player still lost data. Saving will save fake user account.

chocolatecraft commented 3 years ago

Can you make autosaving without save fake user account until load data ?

chocolatecraft commented 3 years ago

I found api. Its has only save all players its not save per player.

chocolatecraft commented 3 years ago

If its has save per player . I will use repeat async auto save after player join server Playerjoinevent

chocolatecraft commented 3 years ago

Can you recode it?

R-Josef commented 3 years ago

I found api. Its has only save all players its not save per player.

I think its has save single player every time they join, i found these in source code, in MainListener at package com.sucy.skill.listener:

    /**
     * Saves player data when they log out and stops passives
     *
     * @param event event details
     */
    @EventHandler(priority = EventPriority.MONITOR)
    public void onQuit(PlayerQuitEvent event)
    {
        unload(event.getPlayer());
    }

    /**
     * Unloads a player's data from the server
     *
     * @param player player to unload
     */
    public static void unload(Player player)
    {
        if (CitizensHook.isNPC(player))
            return;

        boolean skipSaving = false;
        if (loadingPlayers.containsKey(player.getUniqueId())) {
            loadingPlayers.remove(player.getUniqueId()).cancel();
            skipSaving = true;
        }

        PlayerData data = SkillAPI.getPlayerData(player);
        if (SkillAPI.getSettings().isWorldEnabled(player.getWorld()))
        {
            data.record(player);
            data.stopPassives(player);
        }

        FlagManager.clearFlags(player);
        BuffManager.clearData(player);
        Combat.clearData(player);
        DynamicSkill.clearCastData(player);

        player.setDisplayName(player.getName());
        if (VersionManager.isVersionAtLeast(VersionManager.V1_9_0)) {
            player.getAttribute(Attribute.GENERIC_MAX_HEALTH).setBaseValue(20);
        } else {
            player.setMaxHealth(20);
        }
        player.setWalkSpeed(0.2f);
        SkillAPI.unloadPlayerData(player, skipSaving);
    }

skipSaving should be false if this player is not loading data, so go to SkillAPI.unloadPlayerData(OfflinePlayer, boolean) in com.sucy.skill:

    /**
     * Unloads player data from memory, saving it to the config
     * first and then removing it from the map.
     *
     * @param player player to unload data for
     */
    public static void unloadPlayerData(final OfflinePlayer player) {
        unloadPlayerData(player, false);
    }

    public static void unloadPlayerData(final OfflinePlayer player, final boolean skipSaving) {
        if (singleton == null || player == null || singleton.disabling || !singleton.players.containsKey(new VersionPlayer(player).getIdString())) {
            return;
        }

        singleton.getServer().getScheduler().runTaskAsynchronously(singleton, () -> {
            PlayerAccounts accounts = getPlayerAccountData(player);
            if (!skipSaving) {
                singleton.io.saveData(accounts);
            }
            singleton.players.remove(new VersionPlayer(player).getIdString());
        });
    }

singleton is instance of SkillApi class, and io feild is instance of abstract class IOManager in com.sucy.skill.data.io, saveData(OfflinePlayer) is a abstract method, and i think if we use mysql then the IOManager will be SQLIO class's instance in com.sucy.skill.data.io, see saveData(OfflinePlayer) in SQLIO:

    @Override
    public void saveData(PlayerAccounts data)
    {
        SQLConnection connection = openConnection();
        saveSingle(connection, data);
        connection.database.closeConnection();
    }

every thing seems normal, i can't find the key of the problem, same with when player join:

    /**
     * Starts passives and applies class data when a player logs in.
     */
    @EventHandler
    public void onJoin(final PlayerJoinEvent event) {
        final Player player = event.getPlayer();
        if (player.hasMetadata("NPC") || !SkillAPI.getSettings().isWorldEnabled(player.getWorld()))
            return;

        final int delay = SkillAPI.getSettings().getSqlDelay();
        if (SkillAPI.getSettings().isUseSql() && delay > 0) {
            final BukkitTask task = SkillAPI.schedule(() -> {
                try {
                    SkillAPI.reloadPlayerData(player);
                    init(player);
                } finally {
                    loadingPlayers.remove(event.getPlayer().getUniqueId());
                }
            }, delay);
            loadingPlayers.put(event.getPlayer().getUniqueId(), task);
        } else {
            init(player);
        }
    }

and in SkillAPI.reloadPlayerData(OfflinePlayer) method:

    /**
     * Do not use this method outside of onJoin. This will delete any progress a player
     * has made since joining.
     */
    public static void reloadPlayerData(final Player player) {
        doLoad(player);
    }

see doLoad(OfflinePlayer):

    private static PlayerAccounts doLoad(OfflinePlayer player) {
        // Load the data
        PlayerAccounts data = singleton.io.loadData(player);
        singleton.players.put(player.getUniqueId().toString(), data);
        return data;
    }

see loadData(OfflinePlayer) in SQLIO:

    @Override
    public PlayerAccounts loadData(OfflinePlayer player)
    {
        if (player == null) return null;

        SQLConnection connection = openConnection();

        PlayerAccounts result = load(connection, player);

        connection.database.closeConnection();

        return result;
    }
chocolatecraft commented 3 years ago

Its normally for auto save when quit. But auto save period in config its save all player data. I got bug if enabled it some player join while auto save runing. They lost data (if I disable period auto save its worked but when server crash or closed server some player level rollback)

I'am not sure about this I dont have java skill. What method or api can I use with playerjoinevent for repeat async auto save single player? Reference sucy.skill.task savetask.java

Sentropic commented 3 years ago

I read this whole thread and still can't understand what the issue is due to the broken english :P