// Path: variables/src/main/java/com/github/games647/scoreboardstats/variables/ReplaceManager.java
// public class ReplaceManager extends ReplacerAPI {
// 
//     private static final String UNSUPPORTED_VERSION = "The Replacer: {} cannot be registered -" +
//             " the plugin version isn't supported {} {}";
// 
//     //todo: only temporarily
//     @Deprecated
//     private static ReplaceManager instance;
//     private final Plugin plugin;
//     private final BoardManager boardManager;
// 
//     /**
//      * Creates a new replace manager
//      *
//      * @param scoreboardManager to manage the scoreboards
//      * @param plugin            ScoreboardStats plugin
//      */
//     public ReplaceManager(BoardManager scoreboardManager, Plugin plugin, Logger logger) {
//         super(logger);
// 
//         instance = this;
// 
//         this.plugin = plugin;
//         this.boardManager = scoreboardManager;
// 
//         Bukkit.getPluginManager().registerEvents(new PluginListener(this), plugin);
//         addDefaultReplacers();
//     }
// 
//     @Deprecated
//     public static ReplaceManager getInstance() {
//         return instance;
//     }
// 
//     public void close() {
//         instance = null;
//     }
// 
//     @Override
//     public void forceUpdate(Player player, String variable, int score) {
//         boardManager.updateVariable(player, variable, score);
//     }
// 
//     @Override
//     public void forceUpdate(Player player, String variable, String value) {
//         boardManager.updateVariable(player, variable, value);
//     }
// 
//     public void updateGlobals() {
//         replacers.values()
//                 .stream()
//                 .filter(Replacer::isGlobal)
//                 .filter(replacer -> !replacer.isEventVariable())
//                 .forEach(replacer -> {
//                     int score = replacer.scoreReplace(null);
//                     String variable = replacer.getVariable();
//                     Bukkit.getOnlinePlayers().forEach(player -> boardManager.updateVariable(player, variable, score));
//                 });
//     }
// 
//     private void addDefaultReplacers() {
//         Set<String> defaultReplacers = Sets.newHashSet();
//         try {
//             defaultReplacers = ClassPath.from(getClass().getClassLoader())
//                     .getTopLevelClasses("com.github.games647.scoreboardstats.defaults")
//                     .stream()
//                     .map(ClassInfo::load)
//                     .filter(DefaultReplacers.class::isAssignableFrom)
//                     .map(clazz -> (Class<DefaultReplacers<?>>) clazz)
//                     .filter(this::registerDefault)
//                     .map(Class::getSimpleName)
//                     .collect(Collectors.toSet());
//         } catch (IOException ioEx) {
//             logger.error("Failed to register replacers", ioEx);
//         }
// 
//         logger.info("Registered default replacers: {}", defaultReplacers);
//     }
// 
//     private boolean registerDefault(Class<DefaultReplacers<?>> replacerClass) {
//         try {
//             DefaultReplacer annotation = replacerClass.getAnnotation(DefaultReplacer.class);
// 
//             String replacerPluginName = annotation.plugin();
//             if (replacerPluginName.isEmpty()) {
//                 replacerPluginName = plugin.getName();
//             }
// 
//             Plugin replacerPlugin = Bukkit.getPluginManager().getPlugin(replacerPluginName);
//             if (replacerPlugin != null) {
//                 String required = annotation.requiredVersion();
//                 String version = replacerPlugin.getDescription().getVersion();
//                 if (!required.isEmpty() && new Version(version).compareTo(new Version(required)) >= 0) {
//                     logger.info(UNSUPPORTED_VERSION, replacerClass.getSimpleName(), version, required);
//                     return false;
//                 }
// 
//                 Constructor<DefaultReplacers<?>> cons = replacerClass.getConstructor(ReplacerAPI.class, Plugin.class);
//                 cons.newInstance(this, replacerPlugin).register();
//             }
// 
//             return true;
//         } catch (Exception | LinkageError replacerException) {
//             //only catch this throwable, because they could probably happened
//             logger.warn("Cannot register replacer", replacerException);
//         }
// 
//         return false;
//     }
// }


import com.github.games647.scoreboardstats.variables.ReplaceManager;
import java.lang.ref.WeakReference;
import java.time.Instant;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.metadata.FixedMetadataValue;
import org.bukkit.plugin.Plugin;
package com.github.games647.scoreboardstats.pvp;




/**
 * This class is used for loading the player stats.
 */
public class StatsLoader implements Runnable {

    private final Plugin plugin;

    private final WeakReference<Player> weakPlayer;
    private final WeakReference<Database> weakDatabase;

    /**
     * Creates a new loader for a specific player
     *
     * @param plugin        the owning plugin to reschedule
     * @param player        player instance
     * @param statsDatabase the pvp database
     */
    public StatsLoader(Plugin plugin, Player player, Database statsDatabase) {
        this.plugin = plugin;

        //don't prevent the garbage collection of this player if he logs out
        this.weakPlayer = new WeakReference<>(player);
        this.weakDatabase = new WeakReference<>(statsDatabase);
    }

    @Override
    public void run() {
        final Player player = weakPlayer.get();
        Database statsDatabase = weakDatabase.get();
        if (player != null && statsDatabase != null) {
            PlayerStats stats = statsDatabase.loadAccount(player)
                    .orElse(new PlayerStats(player.getUniqueId(), player.getName()));

            //update player name on every load, because it's changeable
            stats.setPlayername(player.getName());
            stats.setLastOnline(Instant.now());

            Bukkit.getScheduler().runTask(plugin, () -> {
                //possible not thread-safe, so reschedule it while setMetadata is thread-safe
                if (player.isOnline()) {
                    //sets it only if the player is only
                    player.setMetadata("player_stats", new FixedMetadataValue(plugin, stats));
// Path: variables/src/main/java/com/github/games647/scoreboardstats/variables/ReplaceManager.java
// public class ReplaceManager extends ReplacerAPI {
// 
//     private static final String UNSUPPORTED_VERSION = "The Replacer: {} cannot be registered -" +
//             " the plugin version isn't supported {} {}";
// 
//     //todo: only temporarily
//     @Deprecated
//     private static ReplaceManager instance;
//     private final Plugin plugin;
//     private final BoardManager boardManager;
// 
//     /**
//      * Creates a new replace manager
//      *
//      * @param scoreboardManager to manage the scoreboards
//      * @param plugin            ScoreboardStats plugin
//      */
//     public ReplaceManager(BoardManager scoreboardManager, Plugin plugin, Logger logger) {
//         super(logger);
// 
//         instance = this;
// 
//         this.plugin = plugin;
//         this.boardManager = scoreboardManager;
// 
//         Bukkit.getPluginManager().registerEvents(new PluginListener(this), plugin);
//         addDefaultReplacers();
//     }
// 
//     @Deprecated
//     public static ReplaceManager getInstance() {
//         return instance;
//     }
// 
//     public void close() {
//         instance = null;
//     }
// 
//     @Override
//     public void forceUpdate(Player player, String variable, int score) {
//         boardManager.updateVariable(player, variable, score);
//     }
// 
//     @Override
//     public void forceUpdate(Player player, String variable, String value) {
//         boardManager.updateVariable(player, variable, value);
//     }
// 
//     public void updateGlobals() {
//         replacers.values()
//                 .stream()
//                 .filter(Replacer::isGlobal)
//                 .filter(replacer -> !replacer.isEventVariable())
//                 .forEach(replacer -> {
//                     int score = replacer.scoreReplace(null);
//                     String variable = replacer.getVariable();
//                     Bukkit.getOnlinePlayers().forEach(player -> boardManager.updateVariable(player, variable, score));
//                 });
//     }
// 
//     private void addDefaultReplacers() {
//         Set<String> defaultReplacers = Sets.newHashSet();
//         try {
//             defaultReplacers = ClassPath.from(getClass().getClassLoader())
//                     .getTopLevelClasses("com.github.games647.scoreboardstats.defaults")
//                     .stream()
//                     .map(ClassInfo::load)
//                     .filter(DefaultReplacers.class::isAssignableFrom)
//                     .map(clazz -> (Class<DefaultReplacers<?>>) clazz)
//                     .filter(this::registerDefault)
//                     .map(Class::getSimpleName)
//                     .collect(Collectors.toSet());
//         } catch (IOException ioEx) {
//             logger.error("Failed to register replacers", ioEx);
//         }
// 
//         logger.info("Registered default replacers: {}", defaultReplacers);
//     }
// 
//     private boolean registerDefault(Class<DefaultReplacers<?>> replacerClass) {
//         try {
//             DefaultReplacer annotation = replacerClass.getAnnotation(DefaultReplacer.class);
// 
//             String replacerPluginName = annotation.plugin();
//             if (replacerPluginName.isEmpty()) {
//                 replacerPluginName = plugin.getName();
//             }
// 
//             Plugin replacerPlugin = Bukkit.getPluginManager().getPlugin(replacerPluginName);
//             if (replacerPlugin != null) {
//                 String required = annotation.requiredVersion();
//                 String version = replacerPlugin.getDescription().getVersion();
//                 if (!required.isEmpty() && new Version(version).compareTo(new Version(required)) >= 0) {
//                     logger.info(UNSUPPORTED_VERSION, replacerClass.getSimpleName(), version, required);
//                     return false;
//                 }
// 
//                 Constructor<DefaultReplacers<?>> cons = replacerClass.getConstructor(ReplacerAPI.class, Plugin.class);
//                 cons.newInstance(this, replacerPlugin).register();
//             }
// 
//             return true;
//         } catch (Exception | LinkageError replacerException) {
//             //only catch this throwable, because they could probably happened
//             logger.warn("Cannot register replacer", replacerException);
//         }
// 
//         return false;
//     }
// }


// Path: pvp/src/main/java/com/github/games647/scoreboardstats/pvp/StatsLoader.java
import com.github.games647.scoreboardstats.variables.ReplaceManager;
import java.lang.ref.WeakReference;
import java.time.Instant;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.metadata.FixedMetadataValue;
import org.bukkit.plugin.Plugin;

package com.github.games647.scoreboardstats.pvp;




/**
 * This class is used for loading the player stats.
 */
public class StatsLoader implements Runnable {

    private final Plugin plugin;

    private final WeakReference<Player> weakPlayer;
    private final WeakReference<Database> weakDatabase;

    /**
     * Creates a new loader for a specific player
     *
     * @param plugin        the owning plugin to reschedule
     * @param player        player instance
     * @param statsDatabase the pvp database
     */
    public StatsLoader(Plugin plugin, Player player, Database statsDatabase) {
        this.plugin = plugin;

        //don't prevent the garbage collection of this player if he logs out
        this.weakPlayer = new WeakReference<>(player);
        this.weakDatabase = new WeakReference<>(statsDatabase);
    }

    @Override
    public void run() {
        final Player player = weakPlayer.get();
        Database statsDatabase = weakDatabase.get();
        if (player != null && statsDatabase != null) {
            PlayerStats stats = statsDatabase.loadAccount(player)
                    .orElse(new PlayerStats(player.getUniqueId(), player.getName()));

            //update player name on every load, because it's changeable
            stats.setPlayername(player.getName());
            stats.setLastOnline(Instant.now());

            Bukkit.getScheduler().runTask(plugin, () -> {
                //possible not thread-safe, so reschedule it while setMetadata is thread-safe
                if (player.isOnline()) {
                    //sets it only if the player is only
                    player.setMetadata("player_stats", new FixedMetadataValue(plugin, stats));
                    ReplaceManager.getInstance().forceUpdate(player, "deaths", stats.getDeaths());