diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 0000000000000000000000000000000000000000..79ee123c2b23e069e35ed634d687e17f731cc702
--- /dev/null
+++ b/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+<component name="ProjectCodeStyleConfiguration">
+  <state>
+    <option name="USE_PER_PROJECT_SETTINGS" value="true" />
+  </state>
+</component>
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c837b276270036c7af4697707718655080551fc7
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,17 @@
+<component name="InspectionProjectProfileManager">
+  <profile version="1.0">
+    <option name="myName" value="Project Default" />
+    <inspection_tool class="AutoCloseableResource" enabled="true" level="WARNING" enabled_by_default="true">
+      <option name="METHOD_MATCHER_CONFIG" value="java.util.Formatter,format,java.io.Writer,append,com.google.common.base.Preconditions,checkNotNull,org.hibernate.Session,close,java.io.PrintWriter,printf,java.io.PrintStream,printf,com.slprojects.slcraftplugin.Main,bddOpenConn" />
+    </inspection_tool>
+    <inspection_tool class="CallToStringConcatCanBeReplacedByOperator" enabled="true" level="WARNING" enabled_by_default="true" />
+    <inspection_tool class="IncorrectFormatting" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
+    <inspection_tool class="ProblematicWhitespace" enabled="true" level="WARNING" enabled_by_default="true" />
+    <inspection_tool class="UnnecessaryQualifierForThis" enabled="false" level="WEAK WARNING" enabled_by_default="false" editorAttributes="INFO_ATTRIBUTES" />
+    <inspection_tool class="UnqualifiedStaticUsage" enabled="false" level="WEAK WARNING" enabled_by_default="false" editorAttributes="INFO_ATTRIBUTES">
+      <option name="m_ignoreStaticFieldAccesses" value="false" />
+      <option name="m_ignoreStaticMethodCalls" value="false" />
+      <option name="m_ignoreStaticAccessFromStaticContext" value="false" />
+    </inspection_tool>
+  </profile>
+</component>
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index f7e4ef9cf330acad5b5cf2a1428a18b8fc966196..63a2e5da1daceaafe6a26062e0205842acc81809 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
 
     <groupId>com.slprojects</groupId>
     <artifactId>SLCraftPlugin</artifactId>
-    <version>1.6.0</version>
+    <version>1.6.1</version>
     <packaging>jar</packaging>
 
     <name>SLCraftPlugin</name>
diff --git a/src/main/java/com/slprojects/slcraftplugin/Main.java b/src/main/java/com/slprojects/slcraftplugin/Main.java
index e0e04cec9a1f67627d2753299f01a6eb07a7e936..93d7608894034dcab252c88b40ef5ddcbb3eaa45 100644
--- a/src/main/java/com/slprojects/slcraftplugin/Main.java
+++ b/src/main/java/com/slprojects/slcraftplugin/Main.java
@@ -1,17 +1,18 @@
 package com.slprojects.slcraftplugin;
 
-import com.slprojects.slcraftplugin.commands.admins.wildReset;
-import com.slprojects.slcraftplugin.commands.publics.linkCode;
-import com.slprojects.slcraftplugin.commands.publics.wild;
-import com.slprojects.slcraftplugin.parallelTasks.playerDataHandler;
-import com.slprojects.slcraftplugin.parallelTasks.internalWebServer;
+import com.slprojects.slcraftplugin.commands.admins.WildReset;
+import com.slprojects.slcraftplugin.commands.publics.LinkCode;
+import com.slprojects.slcraftplugin.commands.publics.Wild;
+import com.slprojects.slcraftplugin.parallelTasks.InternalWebServer;
+import com.slprojects.slcraftplugin.parallelTasks.events.PeriodicEvent;
+import com.slprojects.slcraftplugin.parallelTasks.dataHandlers.PlayerDataHandler;
+import com.slprojects.slcraftplugin.utils.ConsoleLog;
 import me.clip.placeholderapi.PlaceholderAPI;
 import net.luckperms.api.LuckPerms;
 import net.luckperms.api.cacheddata.CachedMetaData;
 import net.md_5.bungee.api.ChatMessageType;
 import net.md_5.bungee.api.chat.TextComponent;
 import org.bukkit.Bukkit;
-import org.bukkit.ChatColor;
 import org.bukkit.Sound;
 import org.bukkit.configuration.file.FileConfiguration;
 import org.bukkit.entity.Player;
@@ -38,28 +39,30 @@ import java.sql.SQLException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
-import java.util.UUID;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 public final class Main extends JavaPlugin implements Listener {
     // Variables
-    private List<UUID> wildCommandActiveUsers;
     private static FileConfiguration config;
-    private static LuckPerms luckPermsApi;
-    public playerDataHandler playerDataHandler;
-    public wild wildCommand;
+    public static LuckPerms luckPermsApi;
+    public static String pluginName;
+
+    // Publiques car on les appelle ailleurs
+    public PlayerDataHandler playerDataHandler;
+    public Wild wildCommand;
+    public PeriodicEvent periodicEvent;
 
-    // Fonctions appelées à des évènements clés
     @Override
     public void onEnable() {
+        pluginName = this.getName();
         // On s'assure qu'on a placeholder api
         if (getServer().getPluginManager().getPlugin("PlaceholderAPI") != null) {
-            getLogger().info("PlaceholderAPI chargé");
+            ConsoleLog.info("PlaceholderAPI chargé");
             // On initialise les listeners
             getServer().getPluginManager().registerEvents(this, this);
         } else {
-            getServer().getConsoleSender().sendMessage(ChatColor.RED+"["+ this.getName() +"] PlaceholderAPI n'est pas accessible!");
+            ConsoleLog.danger("PlaceholderAPI n'est pas accessible!");
             getServer().getPluginManager().disablePlugin(this);
         }
 
@@ -68,14 +71,14 @@ public final class Main extends JavaPlugin implements Listener {
         if (getServer().getPluginManager().getPlugin("LuckPerms") != null) {
             RegisteredServiceProvider<LuckPerms> provider = Bukkit.getServicesManager().getRegistration(LuckPerms.class);
             if (provider != null) {
-                getLogger().info("LuckPerms chargé");
+                ConsoleLog.info("LuckPerms chargé");
                 luckPermsApi = provider.getProvider();
-            }else{
-                getServer().getConsoleSender().sendMessage(ChatColor.RED+"["+ this.getName() +"] LuckPerms n'est pas accessible!");
+            } else {
+                ConsoleLog.danger("LuckPerms n'est pas accessible!");
                 getServer().getPluginManager().disablePlugin(this);
             }
         } else {
-            getServer().getConsoleSender().sendMessage(ChatColor.RED+"["+ this.getName() +"] LuckPerms n'est pas accessible!");
+            ConsoleLog.danger("LuckPerms n'est pas accessible!");
             getServer().getPluginManager().disablePlugin(this);
         }
 
@@ -84,29 +87,30 @@ public final class Main extends JavaPlugin implements Listener {
         reloadConfig();
         config = getConfig();
         updateConfig();
-        playerDataHandler = new playerDataHandler(this);
 
-        // On initialise la base de donnée
         initDatabase();
 
-        wildCommand = new wild(this);
+        playerDataHandler = new PlayerDataHandler(this);
+        InternalWebServer.startServer(this);
+        periodicEvent = new PeriodicEvent(this);
+
+        // On initialise les commandes
+        wildCommand = new Wild(this);
         getCommand("wild").setExecutor(wildCommand);
 
-        wildReset wildReset = new wildReset(this);
+        WildReset wildReset = new WildReset(this);
         getCommand("reset-wild").setExecutor(wildReset);
 
-        linkCode linkCodeCommand = new linkCode(this);
+        LinkCode linkCodeCommand = new LinkCode(this);
         getCommand("getLinkCode").setExecutor(linkCodeCommand);
 
-        internalWebServer.startServer(this);
-
-        getServer().getConsoleSender().sendMessage(ChatColor.GREEN+"SL-Craft | Plugin démarré");
+        ConsoleLog.success("Plugin démarré");
     }
 
     @Override
     public void onDisable() {
         // Plugin shutdown logic
-        getServer().getConsoleSender().sendMessage(ChatColor.RED+"SL-Craft | Plugin éteint");
+        ConsoleLog.danger("Plugin désactivé, au revoir!");
 
         getServer().getOnlinePlayers().forEach(player -> playerDataHandler.quitEvent(player));
     }
@@ -120,13 +124,13 @@ public final class Main extends JavaPlugin implements Listener {
         // On affiche le message de bienvenue
         String welcomeMessage = PlaceholderAPI.setPlaceholders(e.getPlayer(), Objects.requireNonNull(getConfig().getString("player-join-message")));
         // Et on joue un petit son chez tous les joueurs
-        for(Player p : getServer().getOnlinePlayers()){
+        for (Player p : getServer().getOnlinePlayers()) {
             p.sendMessage(welcomeMessage);
-            if(getConfig().getBoolean("player-join-playSound")){
+            if (getConfig().getBoolean("player-join-playSound")) {
                 p.playSound(p.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1, 0);
             }
         }
-        sendMessageToDiscord("**"+e.getPlayer().getName()+"** vient de rejoindre le serveur");
+        sendMessageToDiscord("**" + e.getPlayer().getName() + "** vient de rejoindre le serveur");
     }
 
     @EventHandler(priority = EventPriority.HIGHEST)
@@ -135,14 +139,14 @@ public final class Main extends JavaPlugin implements Listener {
         e.quitMessage(null);
         playerDataHandler.quitEvent(e.getPlayer());
         String quitMessage = PlaceholderAPI.setPlaceholders(e.getPlayer(), Objects.requireNonNull(getConfig().getString("player-quit-message")));
-        for(Player p : getServer().getOnlinePlayers()){
+        for (Player p : getServer().getOnlinePlayers()) {
             p.sendMessage(quitMessage);
         }
-        sendMessageToDiscord("**"+e.getPlayer().getName()+"** a quitté le serveur");
+        sendMessageToDiscord("**" + e.getPlayer().getName() + "** a quitté le serveur");
     }
 
     // On renvoie chaque message des joueurs sur le canal de chat du serveur discord
-    @SuppressWarnings({"unchecked", "deprecation"})
+    @SuppressWarnings({"deprecation"})
     @EventHandler(priority = EventPriority.LOWEST)
     void AsyncChatEvent(AsyncPlayerChatEvent e) {
         String playerFormattedMessage = e.getMessage();
@@ -160,7 +164,7 @@ public final class Main extends JavaPlugin implements Listener {
         playerFormattedMessage = Pattern.compile("~~(.*?)~~").matcher(playerFormattedMessage).replaceAll("§m$1§r ");
 
         // Couleurs
-        playerFormattedMessage = Pattern.compile("&([a-f]|r|[0-8])").matcher(playerFormattedMessage).replaceAll("§$1");
+        playerFormattedMessage = Pattern.compile("&([a-f]|r|[0-9])").matcher(playerFormattedMessage).replaceAll("§$1");
 
         // Ping utilisateur
         Matcher m = Pattern.compile("@(.*?)($|[ ,;:!])").matcher(playerFormattedMessage);
@@ -171,9 +175,9 @@ public final class Main extends JavaPlugin implements Listener {
         // On va chercher le préfix dans LuckPerms
         CachedMetaData playerMetaData = luckPermsApi.getPlayerAdapter(Player.class).getMetaData(e.getPlayer());
 
-        for (Player p: Bukkit.getOnlinePlayers()){
+        for (Player p : Bukkit.getOnlinePlayers()) {
             // Si le joueur a qui on va poster le message (p) a été mentionné
-            if(playerTags.contains(p.getName())){
+            if (playerTags.contains(p.getName())) {
                 // On colorise sa mention
                 playerFormattedMessage = Pattern.compile("@(" + p.getName() + ")($|[ ,;:!])").matcher(playerFormattedMessage).replaceAll("§r§l§d@$1§r$2");
 
@@ -189,8 +193,8 @@ public final class Main extends JavaPlugin implements Listener {
             p.sendMessage(CompleteMessage);
 
             // Et dans la console
-            if(e.getPlayer() == p){
-                getServer().getConsoleSender().sendMessage(CompleteMessage);
+            if (e.getPlayer() == p) {
+                ConsoleLog.info(CompleteMessage);
             }
         }
         // On envoie le message sur discord (on envoie le msg sans les couleur ni le formatage)
@@ -232,7 +236,7 @@ public final class Main extends JavaPlugin implements Listener {
             con.disconnect();
             returnData = response.toString();
         } catch (Exception ex) {
-            getServer().getConsoleSender().sendMessage(ChatColor.RED + "Impossible de se connecter à l'url " + urlString + ". Func getHttp(String urlString)");
+            ConsoleLog.danger("Impossible de se connecter à l'url " + urlString + ". Func getHttp(String urlString)");
             ex.printStackTrace();
         }
 
@@ -241,7 +245,7 @@ public final class Main extends JavaPlugin implements Listener {
 
     // Envoyer un message sur le discord
     @SuppressWarnings({"unchecked"})
-    public void sendMessageToDiscord(String message, String username){
+    public void sendMessageToDiscord(String message, String username) {
         // On va vérifier que le joueur ne fait pas de @everyone ou de @here
         message = message.replace("<@everyone>", "**everyone**");
         message = message.replace("<@here>", "**here**");
@@ -258,51 +262,52 @@ public final class Main extends JavaPlugin implements Listener {
             String urlString = config.getString("discordBot-api-url") + "mc/chat/" + URLEncoder.encode(json.toJSONString(), "UTF-8").replace("+", "%20");
 
             String response = getHttp(urlString);
-            if(getConfig().getBoolean("msg-verbose")){
-                getServer().getConsoleSender().sendMessage("Func AsyncChatEvent(PlayerChatEvent e), HTTP response:" + response);
+            if (getConfig().getBoolean("msg-verbose")) {
+                ConsoleLog.info("Func AsyncChatEvent(PlayerChatEvent e), HTTP response:" + response);
             }
         } catch (UnsupportedEncodingException ex) {
-            getLogger().warning(ChatColor.RED + "Impossible de d'encoder les données. Func AsyncChatEvent(PlayerChatEvent e)");
+            ConsoleLog.danger("Impossible de d'encoder les données. Func AsyncChatEvent(PlayerChatEvent e)");
             ex.printStackTrace();
         }
     }
-    public void sendMessageToDiscord(String message){
+
+    public void sendMessageToDiscord(String message) {
         sendMessageToDiscord(message, "SL-Craft");
     }
 
     public Connection bddOpenConn() { // si mot de passe avec des caractère spéciaux
-        Connection conn=null;
+        Connection conn = null;
         try {
             Class.forName("org.mariadb.jdbc.MariaDbPoolDataSource");
-        } catch (ClassNotFoundException e){
-            getServer().getConsoleSender().sendMessage (ChatColor.RED+"Il manque le driver MariaDB!");
+        } catch (ClassNotFoundException e) {
+            ConsoleLog.danger("Il manque le driver MariaDB!");
             getServer().getPluginManager().disablePlugin(this);
         }
         try {
-            MariaDbPoolDataSource dataSource = new MariaDbPoolDataSource("jdbc:mariadb://"+config.getString("database.host")+"/"+config.getString("database.database")+"?user="+config.getString("database.user")+"&password="+config.getString("database.password")+"&maxPoolSize=10");
+            MariaDbPoolDataSource dataSource = new MariaDbPoolDataSource("jdbc:mariadb://" + config.getString("database.host") + "/" + config.getString("database.database") + "?user=" + config.getString("database.user") + "&password=" + config.getString("database.password") + "&maxPoolSize=10");
             conn = dataSource.getConnection();
-            //getLogger().info(ChatColor.GREEN+"Connexion à la base de données réussie!");
+            ConsoleLog.success("Connexion à la base de données réussie!");
         }// ou les saisir
         catch (SQLException e) {
-            getServer().getConsoleSender().sendMessage(ChatColor.RED+"Erreur lors de la connexion à la base de données.");
+            ConsoleLog.danger("Erreur lors de la connexion à la base de données.");
             getServer().getPluginManager().disablePlugin(this);
         }
         return conn;
     }
-    
-    private void updateConfig(){
-        getLogger().info("Vérification du fichier de configuration...");
+
+    private void updateConfig() {
+        ConsoleLog.info("Vérification du fichier de configuration...");
         // 1.6.0
-        if(!config.contains("server-type")){
-            getLogger().info("Ajout de la variable serverType dans le fichier de configuration...");
+        if (!config.contains("server-type")) {
+            ConsoleLog.info("Ajout de la variable serverType dans le fichier de configuration...");
             config.set("server-type", "dev");
 
             saveConfig();
             reloadConfig();
         }
 
-        if(config.contains("wild") && (config.contains("excluded-biomes") && config.contains("world") && config.contains("max-range"))){
-            getLogger().info("Mise à jour des paramètres concernant la commande /wild");
+        if (config.contains("wild") && (config.contains("excluded-biomes") && config.contains("world") && config.contains("max-range"))) {
+            ConsoleLog.info("Mise à jour des paramètres concernant la commande /wild");
 
             config.set("wild.excluded-biomes", config.get("excluded-biomes"));
             config.set("wild.world", config.get("world"));
@@ -316,19 +321,24 @@ public final class Main extends JavaPlugin implements Listener {
             saveConfig();
             reloadConfig();
         }
+
+        // 1.6.1
+        config.options().copyDefaults(true);
+        saveConfig();
+        reloadConfig();
     }
 
-    private void initDatabase(){
-        try{
+    private void initDatabase() {
+        try {
             Connection con = bddOpenConn();
-            PreparedStatement ps=con.prepareStatement("CREATE TABLE IF NOT EXISTS `site_userSetting` (\n" +
+            PreparedStatement ps = con.prepareStatement("CREATE TABLE IF NOT EXISTS `site_userSetting` (\n" +
                     "  `uuid` varchar(36) NOT NULL DEFAULT '',\n" +
                     "  `name` varchar(128) NOT NULL,\n" +
                     "  `value` text CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,\n" +
                     "  PRIMARY KEY (`uuid`,`name`) USING BTREE\n" +
                     ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;");
             ps.executeQuery();
-            ps=con.prepareStatement("CREATE TABLE IF NOT EXISTS `site_linkCode` (\n" +
+            ps = con.prepareStatement("CREATE TABLE IF NOT EXISTS `site_linkCode` (\n" +
                     " `uuid` VARCHAR(36) NOT NULL,\n" +
                     " `code` VARCHAR(8) NOT NULL,\n" +
                     " `time` TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),\n" +
@@ -338,8 +348,8 @@ public final class Main extends JavaPlugin implements Listener {
                     ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;");
             ps.executeQuery();
             con.close();
-        }catch(Exception e){
-            getServer().getConsoleSender().sendMessage(ChatColor.RED+"Erreur lors de l'exécution de initDatabase(): "+e);
+        } catch (Exception e) {
+            ConsoleLog.danger("Erreur lors de l'exécution de initDatabase(): " + e);
         }
     }
 }
diff --git a/src/main/java/com/slprojects/slcraftplugin/commands/admins/wildReset.java b/src/main/java/com/slprojects/slcraftplugin/commands/admins/WildReset.java
similarity index 61%
rename from src/main/java/com/slprojects/slcraftplugin/commands/admins/wildReset.java
rename to src/main/java/com/slprojects/slcraftplugin/commands/admins/WildReset.java
index 4b199bc8153b075fb3e8982d000b6c32c14e01d3..677fffd3b38a0c15b341a351da8cf6fb6fc3ed70 100644
--- a/src/main/java/com/slprojects/slcraftplugin/commands/admins/wildReset.java
+++ b/src/main/java/com/slprojects/slcraftplugin/commands/admins/WildReset.java
@@ -1,7 +1,7 @@
 package com.slprojects.slcraftplugin.commands.admins;
 
 import com.slprojects.slcraftplugin.Main;
-import org.bukkit.ChatColor;
+import com.slprojects.slcraftplugin.utils.ConsoleLog;
 import org.bukkit.command.Command;
 import org.bukkit.command.CommandExecutor;
 import org.bukkit.command.CommandSender;
@@ -13,20 +13,20 @@ import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
 import java.util.List;
 
-public class wildReset implements CommandExecutor {
+public class WildReset implements CommandExecutor {
     private final Main plugin;
 
-    public wildReset(Main plugin){
+    public WildReset(Main plugin) {
         this.plugin = plugin;
     }
 
     @Override
-    public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args){
-        if(args.length > 0){
-            for(int i=0; i< args.length; i++){
+    public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
+        if (args.length > 0) {
+            for (int i = 0; i < args.length; i++) {
                 Player player = plugin.getServer().getPlayer(args[i]);
-                if(player != null){
-                    List<Object> reset = new ArrayList<Object>(){
+                if (player != null) {
+                    List<Object> reset = new ArrayList<Object>() {
                         {
                             add(0);
                             add(LocalDateTime.parse("2001-12-11 12:30", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")));
@@ -35,26 +35,26 @@ public class wildReset implements CommandExecutor {
                     plugin.playerDataHandler.savePlayerWildCmdStats(player, reset);
                     plugin.wildCommand.setPlayerStats(player, reset);
                     String msg = "Passage de 'wildCmdLastUsed' au 11/12/2001 et 'wildCmdAskNum' à 0 pour " + player.getName() + " UUID: " + player.getUniqueId();
-                    if (sender instanceof Player){
-                        sender.sendMessage("§7§o"+msg);
-                    }else{
-                        plugin.getServer().getConsoleSender().sendMessage(msg);
+                    if (sender instanceof Player) {
+                        sender.sendMessage("§7§o" + msg);
+                    } else {
+                        ConsoleLog.info(msg);
                     }
-                }else{
+                } else {
                     String errorMsg = "Joueur n°" + i + " (dans la liste) non trouvé. :(";
-                    if (sender instanceof Player){
-                        sender.sendMessage("§c"+errorMsg);
-                    }else{
-                        plugin.getServer().getConsoleSender().sendMessage(ChatColor.RED + errorMsg);
+                    if (sender instanceof Player) {
+                        sender.sendMessage("§c" + errorMsg);
+                    } else {
+                        ConsoleLog.danger(errorMsg);
                     }
                 }
             }
-        }else{
+        } else {
             String errorMsg = "Vous devez écrire le pseudo d'un ou plusieurs joueurs.";
-            if (sender instanceof Player){
-                sender.sendMessage("§c"+errorMsg);
-            }else{
-                plugin.getServer().getConsoleSender().sendMessage(ChatColor.RED + errorMsg);
+            if (sender instanceof Player) {
+                sender.sendMessage("§c" + errorMsg);
+            } else {
+                ConsoleLog.danger(errorMsg);
             }
         }
         return true;
diff --git a/src/main/java/com/slprojects/slcraftplugin/commands/publics/linkCode.java b/src/main/java/com/slprojects/slcraftplugin/commands/publics/LinkCode.java
similarity index 78%
rename from src/main/java/com/slprojects/slcraftplugin/commands/publics/linkCode.java
rename to src/main/java/com/slprojects/slcraftplugin/commands/publics/LinkCode.java
index 1633f2d54371ad9f348fe7c4c0f31927308e45dc..de66ecd94735595a3347c575fc0e166ed039cc29 100644
--- a/src/main/java/com/slprojects/slcraftplugin/commands/publics/linkCode.java
+++ b/src/main/java/com/slprojects/slcraftplugin/commands/publics/LinkCode.java
@@ -1,6 +1,7 @@
 package com.slprojects.slcraftplugin.commands.publics;
 
 import com.slprojects.slcraftplugin.Main;
+import com.slprojects.slcraftplugin.utils.ConsoleLog;
 import org.bukkit.ChatColor;
 import org.bukkit.command.Command;
 import org.bukkit.command.CommandExecutor;
@@ -14,25 +15,25 @@ import java.sql.ResultSet;
 import java.time.LocalDateTime;
 import java.util.Random;
 
-public class linkCode implements CommandExecutor {
+public class LinkCode implements CommandExecutor {
 
     // Variables
     private final Main plugin;
 
-    public linkCode(Main plugin){
+    public LinkCode(Main plugin) {
         // On récupère la classe parente pour les paramètres
         this.plugin = plugin;
     }
 
     @Override
-    public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args){
-        if (sender instanceof Player){
+    public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
+        if (sender instanceof Player) {
             Player player = (Player) sender;
 
             // On ouvre la bdd
             Connection con = plugin.bddOpenConn();
 
-            try{
+            try {
                 // On créé le code
                 int leftLimit = 48; // numeral '0'
                 int rightLimit = 122; // letter 'z'
@@ -50,25 +51,25 @@ public class linkCode implements CommandExecutor {
                 rechercheLinkingCode.setString(1, player.getUniqueId().toString());
                 ResultSet resultat = rechercheLinkingCode.executeQuery();
 
-                if(resultat.next()){
+                if (resultat.next()) {
                     PreparedStatement modifyAccountLinkingCode = con.prepareStatement("UPDATE `site_linkCode` SET `code`=?, `time`=?, `used`='0' WHERE `uuid`=?");
                     modifyAccountLinkingCode.setString(1, generatedString);
                     modifyAccountLinkingCode.setString(2, java.sql.Timestamp.valueOf(LocalDateTime.now()).toString());
                     modifyAccountLinkingCode.setString(3, player.getUniqueId().toString());
                     modifyAccountLinkingCode.executeQuery();
 
-                }else{
+                } else {
                     PreparedStatement insertionAccountLinkingCode = con.prepareStatement("INSERT INTO site_linkCode (`uuid`, `code`, `time`, `used`) VALUES (?, ?, ?, '0')");
                     insertionAccountLinkingCode.setString(1, player.getUniqueId().toString());
                     insertionAccountLinkingCode.setString(2, generatedString);
                     insertionAccountLinkingCode.setString(3, java.sql.Timestamp.valueOf(LocalDateTime.now()).toString());
                     insertionAccountLinkingCode.executeQuery();
                 }
-                player.sendMessage("Utilise ce code pour lier ton compte: "+ChatColor.GREEN+generatedString);
-                player.sendMessage(ChatColor.GRAY+"Ce code à usage unique expirera dans 5 minutes.");
-                plugin.getServer().getConsoleSender().sendMessage("Le joueur "+ChatColor.GOLD+player.getName()+ChatColor.RESET+" a généré le code "+ChatColor.GREEN+generatedString+ChatColor.RESET+ChatColor.GRAY+" - Il expirera le "+ java.sql.Timestamp.valueOf(LocalDateTime.now().plusMinutes(5)));
+                player.sendMessage("Utilise ce code pour lier ton compte: " + ChatColor.GREEN + generatedString);
+                player.sendMessage(ChatColor.GRAY + "Ce code à usage unique expirera dans 5 minutes.");
+                ConsoleLog.info("Le joueur " + ChatColor.GOLD + player.getName() + ChatColor.RESET + " a généré le code " + ChatColor.GREEN + generatedString + ChatColor.RESET + ChatColor.GRAY + " - Il expirera le " + java.sql.Timestamp.valueOf(LocalDateTime.now().plusMinutes(5)));
 
-            }catch (Exception e){
+            } catch (Exception e) {
                 e.printStackTrace();
             }
 
diff --git a/src/main/java/com/slprojects/slcraftplugin/commands/publics/wild.java b/src/main/java/com/slprojects/slcraftplugin/commands/publics/Wild.java
similarity index 62%
rename from src/main/java/com/slprojects/slcraftplugin/commands/publics/wild.java
rename to src/main/java/com/slprojects/slcraftplugin/commands/publics/Wild.java
index f22a80655b61f18c97a190cac452a4a45e653469..91360661092677f4cbcae29d3ae6ff6447e783c3 100644
--- a/src/main/java/com/slprojects/slcraftplugin/commands/publics/wild.java
+++ b/src/main/java/com/slprojects/slcraftplugin/commands/publics/Wild.java
@@ -1,6 +1,7 @@
 package com.slprojects.slcraftplugin.commands.publics;
 
 import com.slprojects.slcraftplugin.Main;
+import com.slprojects.slcraftplugin.utils.ConsoleLog;
 import org.bukkit.Bukkit;
 import org.bukkit.ChatColor;
 import org.bukkit.Location;
@@ -19,19 +20,22 @@ import java.util.*;
 
 import static java.lang.Math.abs;
 
-public class wild implements CommandExecutor {
+public class Wild implements CommandExecutor {
 
     // Variables
     private final Main plugin;
 
-    private List<UUID> wildUsersIndexes;
-    private List<LocalDateTime> wildUsersLastAsked;
-    private List<Integer> wildUsersAskNum;
-    private List<Location> wildUsersStartLocation;
+    private final List<UUID> wildUsersIndexes;
+    private final List<LocalDateTime> wildUsersLastAsked;
+    private final List<Integer> wildUsersAskNum;
+    private final List<Location> wildUsersStartLocation;
     private final int usageCooldown;
     private final int usagePerDay;
+    private final List<String> excludedBiomes;
+    private final String wildWorld;
 
-    public wild(Main plugin){
+    @SuppressWarnings("unchecked")
+    public Wild(Main plugin) {
         // On récupère la classe parente pour les paramètres
         this.plugin = plugin;
         wildUsersIndexes = new ArrayList<>();
@@ -40,8 +44,10 @@ public class wild implements CommandExecutor {
         wildUsersStartLocation = new ArrayList<>();
         usageCooldown = plugin.getConfig().getInt("wild.usage-cooldown");
         usagePerDay = plugin.getConfig().getInt("wild.usage-per-day");
+        excludedBiomes = (List<String>) plugin.getConfig().getList("wild.excluded-biomes");
+        wildWorld = plugin.getConfig().getString("wild.world");
 
-        plugin.getServer().getConsoleSender().sendMessage("Instance de wild.");
+        ConsoleLog.info("Instance de wild.");
     }
 
     @Override
@@ -55,106 +61,104 @@ public class wild implements CommandExecutor {
 
             playerIndex = wildUsersIndexes.indexOf(playerUUID);
 
-            if(abs(ChronoUnit.SECONDS.between(wildUsersLastAsked.get(playerIndex), dateTimeNow)) > usageCooldown){
-                if(wildUsersAskNum.get(playerIndex) < usagePerDay){
+            if (abs(ChronoUnit.SECONDS.between(wildUsersLastAsked.get(playerIndex), dateTimeNow)) > usageCooldown) {
+                if (wildUsersAskNum.get(playerIndex) < usagePerDay) {
                     wildUsersLastAsked.set(playerIndex, dateTimeNow);
                     wildUsersStartLocation.set(playerIndex, player.getLocation());
                     askForTeleport(player);
-                }else{
-                    plugin.getServer().getConsoleSender().sendMessage("["+ plugin.getName() +"] Le joueur "+ChatColor.GOLD+player.getName()+ChatColor.RESET+" a exécuté la commande "+ChatColor.GOLD+"/wild"+ChatColor.RESET+" : "+ChatColor.RED+"refusé");
-                    player.sendMessage("§cVous n'avez le droit qu'à §n"+usagePerDay+"§r§c téléportations aléatoires par jour.");
+                } else {
+                    ConsoleLog.info("Le joueur " + ChatColor.GOLD + player.getName() + ChatColor.RESET + " a exécuté la commande " + ChatColor.GOLD + "/wild" + ChatColor.RESET + " : " + ChatColor.RED + "refusé");
+                    player.sendMessage("§cVous n'avez le droit qu'à §n" + usagePerDay + "§r§c téléportations aléatoires par jour.");
                 }
-            }else{
-                plugin.getServer().getConsoleSender().sendMessage("["+ plugin.getName() +"] Le joueur "+ChatColor.GOLD+player.getName()+ChatColor.RESET+" a exécuté la commande "+ChatColor.GOLD+"/wild"+ChatColor.RESET+" : "+ChatColor.RED+"refusé");
-                player.sendMessage("§cVous devez attendre §n"+usageCooldown+"s§r§c avant de relancer la commande.");
+            } else {
+                ConsoleLog.info("Le joueur " + ChatColor.GOLD + player.getName() + ChatColor.RESET + " a exécuté la commande " + ChatColor.GOLD + "/wild" + ChatColor.RESET + " : " + ChatColor.RED + "refusé");
+                player.sendMessage("§cVous devez attendre §n" + usageCooldown + "s§r§c avant de relancer la commande.");
             }
         }
         return true;
     }
 
-    private void askForTeleport(Player player){
+    private void askForTeleport(Player player) {
         int playerIndex = wildUsersIndexes.indexOf(player.getUniqueId());
-        plugin.getServer().getConsoleSender().sendMessage("["+ plugin.getName() +"] Le joueur "+ChatColor.GOLD+player.getName()+ChatColor.RESET+" a exécuté la commande "+ChatColor.GOLD+"/wild"+ChatColor.RESET+" : "+ChatColor.GREEN+"accepté");
+        ConsoleLog.info("Le joueur " + ChatColor.GOLD + player.getName() + ChatColor.RESET + " a exécuté la commande " + ChatColor.GOLD + "/wild" + ChatColor.RESET + " : " + ChatColor.GREEN + "accepté");
         player.sendMessage("Vous allez être téléporté dans §c" + plugin.getConfig().getInt("wild.move-cooldown") + "s§r, ne bougez pas.");
-        int delayInTicks = plugin.getConfig().getInt("wild.move-cooldown") * 20;
+        int delayInTicks = plugin.getConfig().getInt("wild.move-cooldown") * plugin.getConfig().getInt("ticks-per-seconds");
 
         new BukkitRunnable() {
             @Override
-            public void run(){
+            public void run() {
                 Location oldPlayerLocation = wildUsersStartLocation.get(playerIndex);
                 Location newPlayerLocation = player.getLocation();
 
-                if((oldPlayerLocation.getX() != newPlayerLocation.getX()) && (oldPlayerLocation.getY() != newPlayerLocation.getY()) && (oldPlayerLocation.getZ() != newPlayerLocation.getZ())){
+                if ((oldPlayerLocation.getX() != newPlayerLocation.getX()) && (oldPlayerLocation.getY() != newPlayerLocation.getY()) && (oldPlayerLocation.getZ() != newPlayerLocation.getZ())) {
                     player.sendMessage("§cVous avez bougé, téléportation annulée.");
                     // Date bidon pour annuler le cooldown (c'est ma date de naissance :D)
                     wildUsersLastAsked.set(playerIndex, LocalDateTime.parse("2001-12-11 12:30", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")));
-                }else{
+                } else {
                     teleportPlayer(player);
                 }
             }
         }.runTaskLater(plugin, delayInTicks);
     }
 
-    private void teleportPlayer(Player player){
+    // TODO : Executer ceci sur un autre thread -> Le while peut bloquer le serveur.
+    private void teleportPlayer(Player player) {
         int playerIndex = wildUsersIndexes.indexOf(player.getUniqueId());
-        wildUsersAskNum.set(playerIndex, wildUsersAskNum.get(playerIndex)+1);
-
-        // on récupère la liste des biomes exclus
-        List<String> excludedBiomes;
-        excludedBiomes = (List<String>) plugin.getConfig().getList("wild.excluded-biomes");
+        wildUsersAskNum.set(playerIndex, wildUsersAskNum.get(playerIndex) + 1);
 
         player.sendMessage("§6Téléportation vers une coordonnée aléatoire.");
 
         // On défini le radius de téléportation
         Random r = new Random();
-        int low = plugin.getConfig().getInt("wild.max-range")*(-1);
+        int low = plugin.getConfig().getInt("wild.max-range") * (-1);
         int high = plugin.getConfig().getInt("wild.max-range");
 
         // Tant qu'on a un biome non souhaite, on va regérer les coordonnées
-        boolean flag=true;
-        int x=0, z=0, y=0;
-        while(flag){
-            flag=false;
-            x = r.nextInt(high-low) + low;
-            z = r.nextInt(high-low) + low;
-            y = Bukkit.getWorld(plugin.getConfig().getString("wild.world")).getHighestBlockYAt(x, z);
+        boolean flag = true;
+        int x = 0, z = 0, y = 0;
+        while (flag) {
+            flag = false;
+            x = r.nextInt(high - low) + low;
+            z = r.nextInt(high - low) + low;
+            y = Bukkit.getWorld(wildWorld).getHighestBlockYAt(x, z);
             y++; // On incrémente la pos Y pour éviter que le joueur se retrouve dans le sol
 
             for (String excludedBiome : excludedBiomes) {
                 // Biomes non reconnus ou supprimés (deep warm ocean)
-                try{
+                try {
                     Biome.valueOf(excludedBiome.toUpperCase());
-                    if (Bukkit.getWorld(plugin.getConfig().getString("wild.world")).getBiome(x, y, z).equals(Biome.valueOf(excludedBiome.toUpperCase()))) {
+                    if (Bukkit.getWorld(wildWorld).getBiome(x, y, z).equals(Biome.valueOf(excludedBiome.toUpperCase()))) {
                         flag = true;
                     }
-                }catch(Exception ignored){}
+                } catch (Exception ignored) {
+                }
             }
         }
 
         // On téléporte le joueur
-        Location loc = new Location(Bukkit.getWorld(plugin.getConfig().getString("wild.world")), x, y, z, 0, 0);
+        Location loc = new Location(Bukkit.getWorld(wildWorld), x, y, z, 0, 0);
         player.teleport(loc);
 
         int maxVal = Math.max(abs(x), abs(z));
 
-        if(maxVal <= 10000){
+        if (maxVal <= 10000) {
             player.sendMessage("§7§oVous êtes sur un biome généré en 1.16");
-        }else if(maxVal <= 14500){
+        } else if (maxVal <= 14500) {
             player.sendMessage("§7§oVous êtes sur un biome généré en 1.17");
-        }else{
+        } else {
             player.sendMessage("§7§oVous êtes sur un biome généré en 1.18");
         }
-        if((usagePerDay - wildUsersAskNum.get(playerIndex)) > 0){
+        if ((usagePerDay - wildUsersAskNum.get(playerIndex)) > 0) {
             player.sendMessage("§7§oIl vous reste " + (usagePerDay - wildUsersAskNum.get(playerIndex)) + " téléportations pour aujourd'hui.");
-        }else{
+        } else {
             player.sendMessage("§7§oVous avez épuisé toutes vos téléportations du jour.");
         }
     }
 
-    public List<Object> getPlayerStats(Player player){
-        if(!wildUsersIndexes.contains(player.getUniqueId())){
+    public List<Object> getPlayerStats(Player player) {
+        if (!wildUsersIndexes.contains(player.getUniqueId())) {
             return new ArrayList<>();
-        }else{
+        } else {
             int playerIndex = wildUsersIndexes.indexOf(player.getUniqueId());
             // Indexes:
             // - 0: Nombre d'utilisation du jour
@@ -166,10 +170,10 @@ public class wild implements CommandExecutor {
         }
     }
 
-    public void setPlayerStats(Player player, List<Object> stats){
+    public void setPlayerStats(Player player, List<Object> stats) {
         LocalDateTime dateTimeNow = LocalDateTime.now();
 
-        if(!wildUsersIndexes.contains(player.getUniqueId())){
+        if (!wildUsersIndexes.contains(player.getUniqueId())) {
             wildUsersIndexes.add(player.getUniqueId());
             wildUsersLastAsked.add(dateTimeNow);
             wildUsersAskNum.add(0);
@@ -180,12 +184,12 @@ public class wild implements CommandExecutor {
         // Indexes:
         // - 0: Nombre d'utilisation du jour
         // - 1: Date de la dernière commande
-        LocalDateTime savedDateTime = (LocalDateTime)stats.get(1);
-        if(ChronoUnit.HOURS.between(savedDateTime, dateTimeNow) > 24){
+        LocalDateTime savedDateTime = (LocalDateTime) stats.get(1);
+        if (ChronoUnit.HOURS.between(savedDateTime, dateTimeNow) > 24) {
             wildUsersAskNum.set(playerIndex, 0);
             wildUsersLastAsked.set(playerIndex, savedDateTime);
-        }else{
-            wildUsersAskNum.set(playerIndex, (int)stats.get(0));
+        } else {
+            wildUsersAskNum.set(playerIndex, (int) stats.get(0));
             wildUsersLastAsked.set(playerIndex, savedDateTime);
         }
     }
diff --git a/src/main/java/com/slprojects/slcraftplugin/parallelTasks/internalWebServer.java b/src/main/java/com/slprojects/slcraftplugin/parallelTasks/InternalWebServer.java
similarity index 85%
rename from src/main/java/com/slprojects/slcraftplugin/parallelTasks/internalWebServer.java
rename to src/main/java/com/slprojects/slcraftplugin/parallelTasks/InternalWebServer.java
index 1de5e8ab7a2bbf3912cec57107f3a8973be24b95..ad65a2f81bd719e5ce7a4f2dff0944b3c0d3db08 100644
--- a/src/main/java/com/slprojects/slcraftplugin/parallelTasks/internalWebServer.java
+++ b/src/main/java/com/slprojects/slcraftplugin/parallelTasks/InternalWebServer.java
@@ -1,6 +1,7 @@
 package com.slprojects.slcraftplugin.parallelTasks;
 
 import com.slprojects.slcraftplugin.Main;
+import com.slprojects.slcraftplugin.utils.ConsoleLog;
 import org.bukkit.ChatColor;
 import org.bukkit.entity.Player;
 import org.json.simple.JSONObject;
@@ -13,13 +14,13 @@ import java.net.Socket;
 import java.net.URLDecoder;
 import java.net.URLEncoder;
 
-public class internalWebServer {
-    @SuppressWarnings({ "unchecked", "InfiniteLoopStatement" })
-    public static void startServer(Main plugin){
+public class InternalWebServer {
+    @SuppressWarnings({"unchecked", "InfiniteLoopStatement"})
+    public static void startServer(Main plugin) {
         int serverPort = plugin.getConfig().getInt("internal-webserver-port");
 
-        plugin.getServer().getConsoleSender().sendMessage("["+ plugin.getName() +"] Lancement du serveur web intégré sur le port " + ChatColor.GOLD + serverPort);
-        plugin.getServer().getConsoleSender().sendMessage(ChatColor.YELLOW + "["+ plugin.getName() +"] Attention! Le serveur ne fonctionne pas avec les requêtes https!");
+        ConsoleLog.info("Lancement du serveur web intégré sur le port " + ChatColor.GOLD + serverPort);
+        ConsoleLog.warning("Attention! Le serveur ne fonctionne pas avec les requêtes https!");
         // On fait un thread pour écouter le port
         Runnable serverThread = () -> {
             try {
@@ -27,7 +28,7 @@ public class internalWebServer {
                 while (true) {
                     Socket client = serverSocket.accept();
 
-                    //plugin.getServer().getConsoleSender().sendMessage("Nouvelle connexion sur le port " + ChatColor.GOLD + serverPort);
+                    //ConsoleLog.info("Nouvelle connexion sur le port " + ChatColor.GOLD + serverPort);
 
                     // Get input and output streams to talk to the client
                     BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
@@ -101,7 +102,7 @@ public class internalWebServer {
                             break;
                         default:
                             answer.put("status", "error");
-                            answer.put("message", "Commande "+commandName+" inconnue");
+                            answer.put("message", "Commande " + commandName + " inconnue");
                             out.print(answer.toJSONString());
                             break;
                     }
@@ -113,7 +114,7 @@ public class internalWebServer {
                     client.close(); // Close the socket itself
                 }
             } catch (IOException e) {
-                plugin.getServer().getConsoleSender().sendMessage(ChatColor.RED + "["+ plugin.getName() +"] Erreur lors de l'écoute du port " + ChatColor.GOLD  + serverPort);
+                ConsoleLog.danger("Erreur lors de l'écoute du port " + ChatColor.GOLD + serverPort);
                 e.printStackTrace();
 
                 // On va logger le message sur discord
@@ -125,7 +126,7 @@ public class internalWebServer {
                     urlString = plugin.getConfig().getString("discordBot-api-url") + "mc/error/" + URLEncoder.encode(json.toJSONString(), "UTF-8").replace("+", "%20");
                     relaunchListener(plugin);
                 } catch (UnsupportedEncodingException ex) {
-                    plugin.getServer().getConsoleSender().sendMessage(ChatColor.RED + "["+ plugin.getName() +"] Erreur lors de l'encodage du message. Func waitForDiscordMsg::startServer(Main plugin)");
+                    ConsoleLog.danger("Erreur lors de l'encodage du message. Func waitForDiscordMsg::startServer(Main plugin)");
                     ex.printStackTrace();
                 }
                 plugin.getHttp(urlString);
diff --git a/src/main/java/com/slprojects/slcraftplugin/parallelTasks/dataHandlers/PlayedTimeHandler.java b/src/main/java/com/slprojects/slcraftplugin/parallelTasks/dataHandlers/PlayedTimeHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..1d6d374b4e8d2940d55e2eb83c56b8cf1acd05c3
--- /dev/null
+++ b/src/main/java/com/slprojects/slcraftplugin/parallelTasks/dataHandlers/PlayedTimeHandler.java
@@ -0,0 +1,121 @@
+package com.slprojects.slcraftplugin.parallelTasks.dataHandlers;
+
+import com.slprojects.slcraftplugin.Main;
+import com.slprojects.slcraftplugin.parallelTasks.events.GeneralEvents;
+import com.slprojects.slcraftplugin.utils.ConsoleLog;
+import com.slprojects.slcraftplugin.utils.Database;
+import net.luckperms.api.model.group.Group;
+import net.luckperms.api.model.user.User;
+import net.luckperms.api.node.types.InheritanceNode;
+import org.bukkit.ChatColor;
+import org.bukkit.Sound;
+import org.bukkit.entity.Player;
+import org.bukkit.scheduler.BukkitRunnable;
+
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.UUID;
+
+public class PlayedTimeHandler implements dataHandler {
+    private final Main plugin;
+    private final List<UUID> usersIndexes;
+    private final List<LocalDateTime> userSessionJoinDateTime;
+    private final List<Long> userStoredPlayedTimeBeforeJoining;
+    private final int requiredPlayedTimeForUpgradingPlayersAccount;
+    private final Group playersAccountUpgradeGroup;
+
+    public PlayedTimeHandler(Main plugin) {
+        this.plugin = plugin;
+        usersIndexes = new ArrayList<>();
+        userSessionJoinDateTime = new ArrayList<>();
+        userStoredPlayedTimeBeforeJoining = new ArrayList<>();
+        requiredPlayedTimeForUpgradingPlayersAccount = plugin.getConfig().getInt("stats.required-played-time-for-upgrading-players-account");
+        playersAccountUpgradeGroup = Main.luckPermsApi.getGroupManager().getGroup(plugin.getConfig().getString("stats.players-account-upgrade-role"));
+    }
+
+    @Override
+    public void joinEvent(Player player) {
+        usersIndexes.add(player.getUniqueId());
+        userSessionJoinDateTime.add(LocalDateTime.now());
+
+        if (plugin.playerDataHandler.playerAlreadyJoined(player)) {
+            userStoredPlayedTimeBeforeJoining.add(Long.valueOf(Database.getUserSetting(player.getUniqueId().toString(), "playedTime")));
+            // Delay sinon le joueur ne voit pas
+            new BukkitRunnable() {
+                @Override
+                public void run() {
+                    checkPlayerTime(player);
+                }
+            }.runTaskLater(plugin, 20);
+        } else {
+            userStoredPlayedTimeBeforeJoining.add(0L);
+        }
+    }
+
+    @Override
+    public void quitEvent(Player player) {
+        savePlayedTime(player); // On actualise le temps de jeu du joueur
+    }
+
+    public void savePlayedTime(Player player) {
+        // On va calculer le temps de jeu du joueur
+        UUID playerUuid = player.getUniqueId();
+        LocalDateTime timeNow = LocalDateTime.now();
+        Duration duration = Duration.between(timeNow, userSessionJoinDateTime.get(usersIndexes.indexOf(playerUuid)));
+        long playedTimeInSeconds = Math.abs(duration.toSeconds());
+        long actualPlayedTime = userStoredPlayedTimeBeforeJoining.get(usersIndexes.indexOf(playerUuid)) + playedTimeInSeconds;
+
+        Database.setUserSetting(playerUuid.toString(), "playedTime", String.valueOf(actualPlayedTime));
+
+        // Vérification pour avoir le rôle habitué
+        checkPlayerTime(player);
+    }
+
+    public void checkPlayerTime(Player player) {
+        // On va calculer le temps de jeu du joueur
+        UUID playerUuid = player.getUniqueId();
+        LocalDateTime timeNow = LocalDateTime.now();
+        Duration duration = Duration.between(timeNow, userSessionJoinDateTime.get(usersIndexes.indexOf(playerUuid)));
+        long playedTimeInSeconds = Math.abs(duration.toSeconds());
+        long actualPlayedTime = userStoredPlayedTimeBeforeJoining.get(usersIndexes.indexOf(playerUuid)) + playedTimeInSeconds;
+
+        if (actualPlayedTime >= requiredPlayedTimeForUpgradingPlayersAccount) {
+            String playerGroupName = Main.luckPermsApi.getPlayerAdapter(Player.class).getMetaData(player).getPrimaryGroup();
+            if (!Objects.equals(playerGroupName, playersAccountUpgradeGroup.getName())) {
+
+                Group playerGroup = Main.luckPermsApi.getGroupManager().getGroup(playerGroupName);
+                if (playerGroup.getWeight().getAsInt() < playersAccountUpgradeGroup.getWeight().getAsInt()) {
+                    ConsoleLog.info(ChatColor.GREEN + player.getName() + ChatColor.LIGHT_PURPLE + " a débloqué le rôle des joueurs " + ChatColor.GOLD + "habitués" + ChatColor.LIGHT_PURPLE + "!");
+                    User playerLuckPerms = Main.luckPermsApi.getUserManager().getUser(player.getUniqueId());
+
+                    // https://www.spigotmc.org/threads/how-can-i-set-a-players-group-with-luckperms-api.489404/#post-4084060
+                    InheritanceNode node = InheritanceNode.builder(playersAccountUpgradeGroup).value(true).build();
+                    playerLuckPerms.data().add(node);
+                    Main.luckPermsApi.getUserManager().saveUser(playerLuckPerms);
+
+                    int requiredPlayedTimeInHours = requiredPlayedTimeForUpgradingPlayersAccount / 60 / 60;
+                    player.sendMessage(ChatColor.GREEN + "Bravo et un grand merci à toi " + ChatColor.YELLOW + player.getName() + ChatColor.GREEN + "!");
+                    player.sendMessage(ChatColor.GREEN + "Tu as joué pendant plus de" + ChatColor.GOLD + requiredPlayedTimeInHours + "H " + ChatColor.GREEN + "sur le serveur !!!");
+                    player.sendMessage("Pour te récompenser, nous te donnons le rôle des joueurs " + ChatColor.GOLD + "habitués" + ChatColor.RESET + "!");
+                    player.sendMessage(ChatColor.GREEN + "Ce rôle te donne accès à un plus grand nombre de homes et à une plus grande surface utilisable pour protéger tes constructions avec RedProtect.");
+
+                    for (Player connectedPlayer : plugin.getServer().getOnlinePlayers()) {
+                        if (connectedPlayer != player) {
+                            connectedPlayer.sendMessage(ChatColor.GREEN + player.getName() + ChatColor.LIGHT_PURPLE + " a débloqué le rôle des joueurs " + ChatColor.GOLD + "habitués" + ChatColor.LIGHT_PURPLE + "!");
+                        }
+                        player.playSound(player.getLocation(), Sound.UI_TOAST_CHALLENGE_COMPLETE, 100, 2);
+                    }
+
+                    plugin.sendMessageToDiscord("**" + player.getName() + "** a débloqué le rôle des joueurs **habitués**! \uD83E\uDD73");
+                    plugin.sendMessageToDiscord("Un grand merci à toi qui a passé plus de 20H de jeu sur le serveur!  ❤");
+
+                    // Feux d'artifices
+                    GeneralEvents.fireworkSoundEffect(player, plugin);
+                }
+            }
+        }
+    }
+}
diff --git a/src/main/java/com/slprojects/slcraftplugin/parallelTasks/playerDataHandler.java b/src/main/java/com/slprojects/slcraftplugin/parallelTasks/dataHandlers/PlayerDataHandler.java
similarity index 69%
rename from src/main/java/com/slprojects/slcraftplugin/parallelTasks/playerDataHandler.java
rename to src/main/java/com/slprojects/slcraftplugin/parallelTasks/dataHandlers/PlayerDataHandler.java
index e1f8b23c2a48eccded03c844108589d41be4c651..f54650ef1e74cfdeb8c103cbd743255ac0be8d1d 100644
--- a/src/main/java/com/slprojects/slcraftplugin/parallelTasks/playerDataHandler.java
+++ b/src/main/java/com/slprojects/slcraftplugin/parallelTasks/dataHandlers/PlayerDataHandler.java
@@ -1,12 +1,12 @@
-package com.slprojects.slcraftplugin.parallelTasks;
+package com.slprojects.slcraftplugin.parallelTasks.dataHandlers;
 
 import com.slprojects.slcraftplugin.Main;
+import com.slprojects.slcraftplugin.utils.ConsoleLog;
 import org.bukkit.ChatColor;
 import org.bukkit.Statistic;
 import org.bukkit.entity.Player;
 
 import java.sql.*;
-import java.time.Duration;
 import java.time.LocalDateTime;
 import java.time.ZoneOffset;
 import java.time.format.DateTimeFormatter;
@@ -15,46 +15,50 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
 
-public class playerDataHandler {
+public class PlayerDataHandler implements dataHandler {
     private final Main plugin;
     private Connection con;
     // Playtime
-    private final List<UUID> playTimeUsersIndexes;
-    private final List<LocalDateTime> playTimeUsersDate;
+    public final PlayedTimeHandler playedTimeHandler;
+    private final List<UUID> playerIndexes;
+    private final List<Boolean> playerAlreadyJoined;
 
-    public playerDataHandler(Main plugin){
+    public PlayerDataHandler(Main plugin) {
         this.plugin = plugin;
-        playTimeUsersIndexes = new ArrayList<>();
-        playTimeUsersDate = new ArrayList<>();
+        this.playedTimeHandler = new PlayedTimeHandler(plugin);
+        playerIndexes = new ArrayList<>();
+        playerAlreadyJoined = new ArrayList<>();
     }
 
+    @Override
     public void joinEvent(Player player) {
         // On ouvre la bdd
         con = plugin.bddOpenConn();
 
-        playTimeUsersIndexes.add(player.getUniqueId());
-        playTimeUsersDate.add(LocalDateTime.now());
-
-        insertPlayerName(player); // On check si le nom du joueur est déjà enregistré
+        playerIndexes.add(player.getUniqueId());
+        playerAlreadyJoined.add(insertPlayerName(player)); // On check si le nom du joueur est déjà enregistré
         statsPlayerEntryExit(player, true); // On ajoute son entée
         checkPlayerJoinedDate(player); // On check si on dipose de sa date de rejoint
         setPlayerJoinCount(player); // On set le nombre de fois qu'il a rejoint
         plugin.wildCommand.setPlayerStats(player, getPlayerWildCmdStats(player));
 
+        playedTimeHandler.joinEvent(player);
+
         // On ferme la bdd
         try {
             con.close();
         } catch (SQLException e) {
-            plugin.getLogger().warning("Impossible de fermer la connexion à la bdd. Func savePlayerData::saveOnJoin(Player player)");
+            ConsoleLog.warning("Impossible de fermer la connexion à la bdd. Func savePlayerData::saveOnJoin(Player player)");
             e.printStackTrace();
         }
     }
 
+    @Override
     public void quitEvent(Player player) {
         // On ouvre la bdd
         con = plugin.bddOpenConn();
 
-        calculatePlayerPlayTime(player); // On actualise le temps de jeu du joueur
+        playedTimeHandler.quitEvent(player);
         statsPlayerEntryExit(player, false); // On ajoute son sortie
         savePlayerWildCmdStats(player, plugin.wildCommand.getPlayerStats(player));
 
@@ -62,13 +66,14 @@ public class playerDataHandler {
         try {
             con.close();
         } catch (SQLException e) {
-            plugin.getLogger().warning("Impossible de fermer la connexion à la bdd. Func savePlayerData::saveOnQuit(Player player)");
+            ConsoleLog.warning("Impossible de fermer la connexion à la bdd. Func savePlayerData::saveOnQuit(Player player)");
             e.printStackTrace();
         }
     }
 
     // Fonctions
-    private void insertPlayerName(Player player){
+    private boolean insertPlayerName(Player player) {
+        boolean returnValue = false;
         try {
             // On va d'abord regarder si on a déjà renseigné le nom du joueur
             PreparedStatement rechercheUtilisateur = con.prepareStatement("SELECT * FROM site_userSetting WHERE uuid = ? AND name = 'playerName' AND value = ?");
@@ -76,16 +81,17 @@ public class playerDataHandler {
             rechercheUtilisateur.setString(2, player.getName());
             ResultSet resultat = rechercheUtilisateur.executeQuery();
 
-            if(resultat.next()){
+            if (resultat.next()) {
+                returnValue = true;
                 // On a déjà renseigné le nom du joueur on va donc vérifier s'il a besoin d'être mis à jour
-                if(!resultat.getString("value").equals(player.getName())){
+                if (!resultat.getString("value").equals(player.getName())) {
                     // On va mettre à jour le nom du joueur
                     PreparedStatement updateUtilisateur = con.prepareStatement("UPDATE site_userSetting SET value = ? WHERE uuid = ? AND name = 'playerName'");
                     updateUtilisateur.setString(1, player.getName());
                     updateUtilisateur.setString(2, player.getUniqueId().toString());
                     updateUtilisateur.executeUpdate();
                 }
-            }else{
+            } else {
                 // On peut insérer le nom du joueur
                 PreparedStatement insertUtilisateur = con.prepareStatement("INSERT INTO site_userSetting (uuid, name, value) VALUES (?, 'playerName', ?)");
                 insertUtilisateur.setString(1, player.getUniqueId().toString());
@@ -93,12 +99,13 @@ public class playerDataHandler {
                 insertUtilisateur.executeQuery();
             }
         } catch (SQLException e) {
-            plugin.getLogger().warning("Func savePlayerData::insertPlayerName(Player player)");
+            ConsoleLog.warning("Func savePlayerData::insertPlayerName(Player player)");
             e.printStackTrace();
         }
+        return returnValue;
     }
 
-    private void statsPlayerEntryExit(Player player, boolean isEnter){
+    private void statsPlayerEntryExit(Player player, boolean isEnter) {
         try {
             PreparedStatement insertPlayerEntryOrExit = con.prepareStatement("INSERT INTO site_playerEntries (uuid, isJoin, date) VALUES (?, ?, ?)");
             insertPlayerEntryOrExit.setString(1, player.getUniqueId().toString());
@@ -106,28 +113,28 @@ public class playerDataHandler {
             insertPlayerEntryOrExit.setString(3, Timestamp.valueOf(java.time.LocalDateTime.now()).toString());
             insertPlayerEntryOrExit.executeQuery();
         } catch (SQLException e) {
-            plugin.getLogger().warning("Func savePlayerData::playerAddPlayerEntryOrExit(Player player, boolean isEnter)");
+            ConsoleLog.warning("Func savePlayerData::playerAddPlayerEntryOrExit(Player player, boolean isEnter)");
             e.printStackTrace();
         }
     }
 
-    private void checkPlayerJoinedDate(Player player){
+    private void checkPlayerJoinedDate(Player player) {
         try {
             // On va vérifier si on l'a déjà renseigné par le passé
             PreparedStatement rechercheUtilisateur = con.prepareStatement("SELECT * FROM site_userSetting WHERE uuid = ? AND name = 'joinedDate'");
             rechercheUtilisateur.setString(1, player.getUniqueId().toString());
             ResultSet resultat = rechercheUtilisateur.executeQuery();
 
-            if(!resultat.next()){
+            if (!resultat.next()) {
                 // On n'a pas renseigné la date de création du joueur
-                if(player.hasPlayedBefore()){
+                if (player.hasPlayedBefore()) {
                     // On va piocher la date d'inscription chez CoreProtect (si elle existe)
                     // On la prend chez CoreProtect car le plugin a été installé dans les premières semaines du serveur. Il a donc bcp plus de données que nous concernant les anciens joueurs.
                     PreparedStatement rechercheDateInscription = con.prepareStatement("SELECT time FROM co_user WHERE uuid = ?");
                     rechercheDateInscription.setString(1, player.getUniqueId().toString());
                     resultat = rechercheDateInscription.executeQuery();
 
-                    if(resultat.next()){
+                    if (resultat.next()) {
                         // On insère la date d'inscription
                         PreparedStatement insertionDateInscription = con.prepareStatement("INSERT INTO site_userSetting (`uuid`, `name`, `value`) VALUES (?,'joinedDate',?)");
                         insertionDateInscription.setString(1, player.getUniqueId().toString());
@@ -135,7 +142,7 @@ public class playerDataHandler {
                         insertionDateInscription.executeQuery();
 
                         // On va précisier que la date d'inscription a été trouvée chez CoreProtect
-                        plugin.getLogger().info("Le joueur "+ ChatColor.GOLD+player.getName()+ChatColor.RESET+" n'avait pas de données sur sa date d'inscription dans dans la table des paramètres utilisateurs. On lui a donc attribué comme date de création du compte, celle que détenait CoreProtect.");
+                        ConsoleLog.info("Le joueur " + ChatColor.GOLD + player.getName() + ChatColor.RESET + " n'avait pas de données sur sa date d'inscription dans dans la table des paramètres utilisateurs. On lui a donc attribué comme date de création du compte, celle que détenait CoreProtect.");
                     } else {
                         // On insère la date d'inscription (du coup on considère que Le joueur n'a pas joué avant, malgré la condition)
                         PreparedStatement insertionDateInscription = con.prepareStatement("INSERT INTO site_userSetting (`uuid`, `name`, `value`) VALUES (?,'joinedDate',?)");
@@ -151,9 +158,9 @@ public class playerDataHandler {
 
                         // On est daccord que ceci n'est pas censé arriver, cela ne concerne que mes potes n'étant venus que durant les premières semaines du serveur.
 
-                        plugin.getLogger().info("Le joueur "+ChatColor.GOLD+player.getName()+ChatColor.RESET+" n'avait pas de données sur sa date d'inscription dans dans la table des paramètres utilisateurs, ni dans la table des utilisateurs de CoreProtect. On lui a donc attribué comme date de création du compte, la date du début de sa partie.");
+                        ConsoleLog.info("Le joueur " + ChatColor.GOLD + player.getName() + ChatColor.RESET + " n'avait pas de données sur sa date d'inscription dans dans la table des paramètres utilisateurs, ni dans la table des utilisateurs de CoreProtect. On lui a donc attribué comme date de création du compte, la date du début de sa partie.");
                     }
-                }else{
+                } else {
                     // Le joueur est nouveau, on insère la date d'inscription
                     PreparedStatement insertionDateInscription = con.prepareStatement("INSERT INTO site_userSetting (`uuid`, `name`, `value`) VALUES (?,'joinedDate',?)");
                     insertionDateInscription.setString(1, player.getUniqueId().toString());
@@ -162,25 +169,25 @@ public class playerDataHandler {
                 }
             }
         } catch (SQLException e) {
-            plugin.getLogger().warning("Func savePlayerData::checkJoinedDate(Player player)");
+            ConsoleLog.warning("Func savePlayerData::checkJoinedDate(Player player)");
             e.printStackTrace();
         }
     }
 
-    void setPlayerJoinCount(Player player){
-        try{
+    void setPlayerJoinCount(Player player) {
+        try {
             // On va vérifier si on a déjà renseigné le nombre de fois que le joueur a rejoint le serveur par le passé
             PreparedStatement rechercheJoinCount = con.prepareStatement("SELECT * FROM site_userSetting WHERE uuid = ? AND name = 'joins'");
             rechercheJoinCount.setString(1, player.getUniqueId().toString());
             ResultSet resultat = rechercheJoinCount.executeQuery();
 
-            if(resultat.next()){
+            if (resultat.next()) {
                 // On a déjà renseigné ça par le passé, on va donc faire un update
                 PreparedStatement updateJoinCount = con.prepareStatement("UPDATE site_userSetting SET value = ? WHERE uuid = ? AND name = 'joins'");
                 updateJoinCount.setString(1, String.valueOf(player.getStatistic(Statistic.LEAVE_GAME) + 1));
                 updateJoinCount.setString(2, player.getUniqueId().toString());
                 updateJoinCount.executeQuery();
-            }else{
+            } else {
                 // On n'a pas encore renseigné le nombre de fois que le joueur a rejoint le serveur, on va donc faire un insert
                 PreparedStatement insertionJoinCount = con.prepareStatement("INSERT INTO site_userSetting (`uuid`, `name`, `value`) VALUES (?,'joins',?)");
                 insertionJoinCount.setString(1, player.getUniqueId().toString());
@@ -189,44 +196,12 @@ public class playerDataHandler {
             }
 
         } catch (SQLException e) {
-            plugin.getLogger().warning("Func savePlayerData::setPlayerJoinCount(Player player)");
-            e.printStackTrace();
-        }
-    }
-
-    private void calculatePlayerPlayTime(Player player){
-        // On va calculer le temps de jeu du joueur
-        LocalDateTime timeNow = LocalDateTime.now();
-        Duration duration = Duration.between(timeNow, playTimeUsersDate.get(playTimeUsersIndexes.indexOf(player.getUniqueId())));
-        long playedTimeInSeconds = Math.abs(duration.toSeconds());
-
-        try{
-            // On va vérifier si on a déjà renseigné le temps de jeu du joueur par le passé
-            PreparedStatement recherchePlayTime = con.prepareStatement("SELECT * FROM site_userSetting WHERE uuid = ? AND name = 'playedTime'");
-            recherchePlayTime.setString(1, player.getUniqueId().toString());
-            ResultSet resultat = recherchePlayTime.executeQuery();
-
-            if(resultat.next()){
-                // On a déjà renseigné ça par le passé, on va donc faire un update
-                PreparedStatement updatePlayTime = con.prepareStatement("UPDATE site_userSetting SET value = ? WHERE uuid = ? AND name = 'playedTime'");
-                updatePlayTime.setString(1, String.valueOf(Long.parseLong(resultat.getString("value")) + playedTimeInSeconds));
-                updatePlayTime.setString(2, player.getUniqueId().toString());
-                updatePlayTime.executeQuery();
-            }else{
-                // On n'a pas encore renseigné le temps de jeu du joueur, on va donc faire un insert
-                PreparedStatement insertionPlayTime = con.prepareStatement("INSERT INTO site_userSetting (`uuid`, `name`, `value`) VALUES (?,'playedTime',?)");
-                insertionPlayTime.setString(1, player.getUniqueId().toString());
-                insertionPlayTime.setString(2, String.valueOf(playedTimeInSeconds));
-                insertionPlayTime.executeQuery();
-            }
-
-        } catch (SQLException e) {
-            plugin.getLogger().warning("Func savePlayerData::increasePlayerPlayTime(Player player)");
+            ConsoleLog.warning("Func savePlayerData::setPlayerJoinCount(Player player)");
             e.printStackTrace();
         }
     }
 
-    private List<Object> getPlayerWildCmdStats(Player player){
+    private List<Object> getPlayerWildCmdStats(Player player) {
         // Indexes:
         // - 0: Nombre d'utilisation du jour
         // - 1: Date de la dernière commande
@@ -236,32 +211,32 @@ public class playerDataHandler {
             playerLastUsed.setString(1, player.getUniqueId().toString());
             ResultSet lastUsedResult = playerLastUsed.executeQuery();
 
-            if(lastUsedResult.next()){
+            if (lastUsedResult.next()) {
                 LocalDateTime lastUsed = Timestamp.valueOf(lastUsedResult.getString("value")).toLocalDateTime();
-                if(ChronoUnit.HOURS.between(lastUsed, LocalDateTime.now()) > 24){
-                    return new ArrayList<Object>(){
+                if (ChronoUnit.HOURS.between(lastUsed, LocalDateTime.now()) > 24) {
+                    return new ArrayList<Object>() {
                         {
                             add(0);
                             add(lastUsed);
                         }
                     };
-                }else{
+                } else {
                     PreparedStatement playerAskNum = con.prepareStatement("SELECT * FROM site_userSetting WHERE uuid = ? AND name = 'wildCmdAskNum'");
                     playerAskNum.setString(1, player.getUniqueId().toString());
                     ResultSet askNumResult = playerAskNum.executeQuery();
 
-                    if(askNumResult.next()){
-                        return new ArrayList<Object>(){
+                    if (askNumResult.next()) {
+                        return new ArrayList<Object>() {
                             {
                                 add(Integer.valueOf(askNumResult.getString("value")));
                                 add(lastUsed);
                             }
                         };
-                    }else{
-                        plugin.getLogger().warning("Func savePlayerData::getPlayerWildCmdStats(Player player)");
-                        plugin.getLogger().warning("Fonctionnement anormal! On dispose de la date de 'wildCmdLastUsed' mais pas de 'wildCmdAskNum' pour le joueur " + player.getName() + " UUID: " + player.getUniqueId());
-                        plugin.getLogger().warning("Passage de 'wildCmdAskNum' à 0.");
-                        return new ArrayList<Object>(){
+                    } else {
+                        ConsoleLog.warning("Func savePlayerData::getPlayerWildCmdStats(Player player)");
+                        ConsoleLog.warning("Fonctionnement anormal! On dispose de la date de 'wildCmdLastUsed' mais pas de 'wildCmdAskNum' pour le joueur " + player.getName() + " UUID: " + player.getUniqueId());
+                        ConsoleLog.warning("Passage de 'wildCmdAskNum' à 0.");
+                        return new ArrayList<Object>() {
                             {
                                 add(0);
                                 add(lastUsed);
@@ -269,9 +244,9 @@ public class playerDataHandler {
                         };
                     }
                 }
-            }else{
+            } else {
                 plugin.getLogger().info("Mise à jour du joueur " + player.getName() + " UUID: " + player.getUniqueId());
-                plugin.getLogger().info("Création des champs 'wildCmdLastUsed' et 'wildCmdAskNum'");
+                ConsoleLog.info("Création des champs 'wildCmdLastUsed' et 'wildCmdAskNum'");
 
                 // On va insérer une date bidon pour éviter un potentiel cooldown
                 LocalDateTime dateBidon = LocalDateTime.parse("2001-12-11 12:30", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"));
@@ -285,7 +260,7 @@ public class playerDataHandler {
                 insertWildCmdAskNum.setString(2, "0");
                 insertWildCmdAskNum.executeQuery();
 
-                return new ArrayList<Object>(){
+                return new ArrayList<Object>() {
                     {
                         add(0);
                         add(dateBidon);
@@ -294,15 +269,15 @@ public class playerDataHandler {
             }
 
         } catch (SQLException e) {
-            plugin.getLogger().warning("Func savePlayerData::getPlayerWildCmdStats(Player player)");
+            ConsoleLog.warning("Func savePlayerData::getPlayerWildCmdStats(Player player)");
             e.printStackTrace();
         }
 
-        plugin.getLogger().warning("Func savePlayerData::getPlayerWildCmdStats(Player player)");
-        plugin.getLogger().warning("Fonctionnement anormal! La recherche dans la bdd a échouée pour le joueur " + player.getName() + " UUID: " + player.getUniqueId());
-        plugin.getLogger().warning("Passage de 'wildCmdLastUsed' au 11 décembre 2001 et 'wildCmdAskNum' à 0");
+        ConsoleLog.warning("Func savePlayerData::getPlayerWildCmdStats(Player player)");
+        ConsoleLog.warning("Fonctionnement anormal! La recherche dans la bdd a échouée pour le joueur " + player.getName() + " UUID: " + player.getUniqueId());
+        ConsoleLog.warning("Passage de 'wildCmdLastUsed' au 11 décembre 2001 et 'wildCmdAskNum' à 0");
 
-        return new ArrayList<Object>(){
+        return new ArrayList<Object>() {
             {
                 add(0);
                 add(LocalDateTime.parse("2001-12-11 12:30", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")));
@@ -310,7 +285,7 @@ public class playerDataHandler {
         };
     }
 
-    public void savePlayerWildCmdStats(Player player, List<Object> stats){
+    public void savePlayerWildCmdStats(Player player, List<Object> stats) {
         // Indexes:
         // - 0: Nombre d'utilisation du jour
         // - 1: Date de la dernière commande
@@ -323,13 +298,17 @@ public class playerDataHandler {
             updateWildCmdAskNum.executeUpdate();
 
             PreparedStatement updateWildCmdLastUsed = con.prepareStatement("UPDATE site_userSetting SET value = ? WHERE uuid = ? AND name = 'wildCmdLastUsed'");
-            updateWildCmdLastUsed.setString(1, Timestamp.valueOf((LocalDateTime)stats.get(1)).toString());
+            updateWildCmdLastUsed.setString(1, Timestamp.valueOf((LocalDateTime) stats.get(1)).toString());
             updateWildCmdLastUsed.setString(2, player.getUniqueId().toString());
             updateWildCmdLastUsed.executeUpdate();
 
         } catch (SQLException e) {
-            plugin.getLogger().warning("Func savePlayerData::getPlayerWildCmdStats(Player player)");
+            ConsoleLog.warning("Func savePlayerData::getPlayerWildCmdStats(Player player)");
             e.printStackTrace();
         }
     }
+
+    public boolean playerAlreadyJoined(Player player) {
+        return playerAlreadyJoined.get(playerIndexes.indexOf(player.getUniqueId()));
+    }
 }
diff --git a/src/main/java/com/slprojects/slcraftplugin/parallelTasks/dataHandlers/dataHandler.java b/src/main/java/com/slprojects/slcraftplugin/parallelTasks/dataHandlers/dataHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..18fbe2c12901f87d9de80df280de81c5181ebac7
--- /dev/null
+++ b/src/main/java/com/slprojects/slcraftplugin/parallelTasks/dataHandlers/dataHandler.java
@@ -0,0 +1,9 @@
+package com.slprojects.slcraftplugin.parallelTasks.dataHandlers;
+
+import org.bukkit.entity.Player;
+
+public interface dataHandler {
+    void joinEvent(Player player);
+
+    void quitEvent(Player player);
+}
diff --git a/src/main/java/com/slprojects/slcraftplugin/parallelTasks/events/GeneralEvents.java b/src/main/java/com/slprojects/slcraftplugin/parallelTasks/events/GeneralEvents.java
new file mode 100644
index 0000000000000000000000000000000000000000..fe0ed5bfe46530b8fe74f86cf77f0b71f483b1d3
--- /dev/null
+++ b/src/main/java/com/slprojects/slcraftplugin/parallelTasks/events/GeneralEvents.java
@@ -0,0 +1,33 @@
+package com.slprojects.slcraftplugin.parallelTasks.events;
+
+import com.slprojects.slcraftplugin.Main;
+import org.bukkit.Sound;
+import org.bukkit.entity.Player;
+import org.bukkit.scheduler.BukkitRunnable;
+
+import java.util.Random;
+
+public class GeneralEvents {
+    public static void fireworkSoundEffect(Player player, Main plugin) {
+        int min = 1;
+        int max = 25;
+        Random random = new Random();
+
+        for (int i = 0; i < 6; i++) {
+            int delay = random.nextInt(max - min + 1) + min;
+            new BukkitRunnable() {
+                @Override
+                public void run() {
+                    player.playSound(player.getLocation(), Sound.ENTITY_FIREWORK_ROCKET_LAUNCH, 100, 2);
+                }
+            }.runTaskLater(plugin, delay);
+
+            new BukkitRunnable() {
+                @Override
+                public void run() {
+                    player.playSound(player.getLocation(), Sound.ENTITY_FIREWORK_ROCKET_TWINKLE, 100, 2);
+                }
+            }.runTaskLater(plugin, delay + 40);
+        }
+    }
+}
diff --git a/src/main/java/com/slprojects/slcraftplugin/parallelTasks/events/PeriodicEvent.java b/src/main/java/com/slprojects/slcraftplugin/parallelTasks/events/PeriodicEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..ec65540fe63204d8d0e30fa2ecdcc14da491d332
--- /dev/null
+++ b/src/main/java/com/slprojects/slcraftplugin/parallelTasks/events/PeriodicEvent.java
@@ -0,0 +1,42 @@
+package com.slprojects.slcraftplugin.parallelTasks.events;
+
+import com.slprojects.slcraftplugin.Main;
+import org.bukkit.entity.Player;
+import org.bukkit.scheduler.BukkitRunnable;
+
+public class PeriodicEvent {
+    private final Main plugin;
+    private boolean doesTheEventIsCurrentlyRunning = false;
+    private final int periodicEventCallTime;
+
+    public PeriodicEvent(Main plugin) {
+        this.plugin = plugin;
+        startPeriodicEvent();
+        periodicEventCallTime = plugin.getConfig().getInt("periodic-event-call-time") * plugin.getConfig().getInt("ticks-per-seconds");
+    }
+
+    public void startPeriodicEvent() {
+        if (doesTheEventIsCurrentlyRunning)
+            throw new RuntimeException("L'exécution de l'évènement périodique est déjà enclanchée.");
+
+        doesTheEventIsCurrentlyRunning = true;
+
+        new BukkitRunnable() {
+            @Override
+            public void run() {
+                periodicEvent();
+            }
+        }.runTaskLater(plugin, (periodicEventCallTime));
+    }
+
+    private void periodicEvent() {
+        for (Player player : plugin.getServer().getOnlinePlayers()) {
+            plugin.playerDataHandler.playedTimeHandler.savePlayedTime(player);
+        }
+        //ConsoleLog.warning("[SL-Craft] Évènement périodique éxecuté.");
+
+        // S'exécute à la fin
+        doesTheEventIsCurrentlyRunning = false;
+        startPeriodicEvent();
+    }
+}
diff --git a/src/main/java/com/slprojects/slcraftplugin/utils/ConsoleLog.java b/src/main/java/com/slprojects/slcraftplugin/utils/ConsoleLog.java
new file mode 100644
index 0000000000000000000000000000000000000000..e863c61c18cf348ef50692ae89279a36f8bd4c10
--- /dev/null
+++ b/src/main/java/com/slprojects/slcraftplugin/utils/ConsoleLog.java
@@ -0,0 +1,24 @@
+package com.slprojects.slcraftplugin.utils;
+
+import com.slprojects.slcraftplugin.Main;
+import org.bukkit.ChatColor;
+
+import static org.bukkit.Bukkit.getServer;
+
+public class ConsoleLog {
+    public static void info(String message) {
+        getServer().getConsoleSender().sendMessage("[" + Main.pluginName + "] " + message);
+    }
+
+    public static void warning(String message) {
+        getServer().getConsoleSender().sendMessage(ChatColor.GOLD + "[" + Main.pluginName + "] " + message);
+    }
+
+    public static void danger(String message) {
+        getServer().getConsoleSender().sendMessage(ChatColor.RED + "[" + Main.pluginName + "] " + message);
+    }
+
+    public static void success(String message) {
+        getServer().getConsoleSender().sendMessage(ChatColor.GREEN + "[" + Main.pluginName + "] " + message);
+    }
+}
diff --git a/src/main/java/com/slprojects/slcraftplugin/utils/Database.java b/src/main/java/com/slprojects/slcraftplugin/utils/Database.java
new file mode 100644
index 0000000000000000000000000000000000000000..54aee618c3e5d706f2aecd117f6b68a14ac6cefb
--- /dev/null
+++ b/src/main/java/com/slprojects/slcraftplugin/utils/Database.java
@@ -0,0 +1,122 @@
+package com.slprojects.slcraftplugin.utils;
+
+import com.slprojects.slcraftplugin.Main;
+import org.bukkit.configuration.file.FileConfiguration;
+import org.mariadb.jdbc.MariaDbPoolDataSource;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import static org.bukkit.Bukkit.getServer;
+
+@SuppressWarnings("UnusedReturnValue")
+public class Database {
+    static final private String userSettingsTabName = "site_userSetting";
+
+    public static String getUserSetting(String uuid, String key) {
+        Connection con = bddOpenConn();
+        String returnValue = null;
+
+        try {
+            PreparedStatement query = con.prepareStatement("SELECT * FROM " + userSettingsTabName + " WHERE uuid = ? AND name = ?");
+            query.setString(1, uuid);
+            query.setString(2, key);
+            ResultSet resultat = query.executeQuery();
+
+            if (resultat.next()) {
+                returnValue = resultat.getString("value");
+            }
+        } catch (SQLException e) {
+            ConsoleLog.danger("Erreur lors de l'exécution de la requête sql." + e);
+        }
+
+        // On ferme la bdd
+        try {
+            con.close();
+        } catch (SQLException e) {
+            ConsoleLog.danger("Impossible de fermer la connexion à la bdd.");
+            e.printStackTrace();
+        }
+        return returnValue;
+    }
+
+    public static boolean setUserSetting(String uuid, String key, String value) {
+        Connection con = bddOpenConn();
+        boolean isOperationASuccess = false;
+        boolean isEntryExists = (getUserSetting(uuid, key) != null);
+
+        try {
+            if (isEntryExists) {
+                PreparedStatement updateEntry = con.prepareStatement("UPDATE site_userSetting SET value = ? WHERE uuid = ? AND name = ?");
+                updateEntry.setString(1, value);
+                updateEntry.setString(2, uuid);
+                updateEntry.setString(3, key);
+                updateEntry.executeUpdate();
+                isOperationASuccess = true;
+            } else {
+                isOperationASuccess = insertUserSettingEntry(uuid, key, value);
+            }
+        } catch (SQLException e) {
+            ConsoleLog.danger("Erreur lors de l'exécution de la requête sql." + e);
+        }
+
+        // On ferme la bdd
+        try {
+            con.close();
+        } catch (SQLException e) {
+            ConsoleLog.danger("Impossible de fermer la connexion à la bdd.");
+            e.printStackTrace();
+        }
+
+        return isOperationASuccess;
+    }
+
+    private static boolean insertUserSettingEntry(String uuid, String key, String value) {
+        Connection con = bddOpenConn();
+        boolean isOperationASuccess = false;
+
+        try {
+            PreparedStatement insertEntry = con.prepareStatement("INSERT INTO site_userSetting (uuid, name, value) VALUES (?, ?, ?)");
+            insertEntry.setString(1, uuid);
+            insertEntry.setString(2, key);
+            insertEntry.setString(3, value);
+            insertEntry.executeQuery();
+            isOperationASuccess = true;
+        } catch (SQLException e) {
+            ConsoleLog.danger("Erreur lors de l'exécution de la requête sql." + e);
+        }
+
+        // On ferme la bdd
+        try {
+            con.close();
+        } catch (SQLException e) {
+            ConsoleLog.danger("Impossible de fermer la connexion à la bdd.");
+            e.printStackTrace();
+        }
+
+        return isOperationASuccess;
+    }
+
+    public static Connection bddOpenConn() {
+        FileConfiguration config = getServer().getPluginManager().getPlugin(Main.pluginName).getConfig();
+        Connection conn = null;
+
+        try {
+            Class.forName("org.mariadb.jdbc.MariaDbPoolDataSource");
+        } catch (ClassNotFoundException e) {
+            ConsoleLog.danger("Il manque le driver MariaDB!");
+        }
+
+        try {
+            MariaDbPoolDataSource dataSource = new MariaDbPoolDataSource("jdbc:mariadb://" + config.getString("database.host") + "/" + config.getString("database.database") + "?user=" + config.getString("database.user") + "&password=" + config.getString("database.password") + "&maxPoolSize=10");
+            conn = dataSource.getConnection();
+            //ConsoleLog.success("Connexion à la base de données réussie!");
+        } catch (SQLException e) {
+            ConsoleLog.danger("Erreur lors de la connexion à la base de données.");
+        }
+
+        return conn;
+    }
+}
diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml
index 545d91fc007eab14046124467cc1264d1eac5bc4..29e2f146cd2ac05e4406db235daf0ff85e9ae215 100644
--- a/src/main/resources/config.yml
+++ b/src/main/resources/config.yml
@@ -38,4 +38,13 @@ msg-verbose: false
 discordBot-api-url: "http://node.sl-projects.com:27001/"
 
 # Config générale
-server-type: prod # prod/dev
\ No newline at end of file
+server-type: prod # prod/dev
+
+# Periodic events
+periodic-event-call-time: 60 # En sencondes
+ticks-per-seconds: 20
+
+# Statistiques des joueurs
+stats:
+  required-played-time-for-upgrading-players-account: 72000 # En sencondes
+  players-account-upgrade-role: "habitué"
\ No newline at end of file
diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml
index 85da917c30a52d0f4be08aca37891b0491ad56c6..ec17e4a73a62243ddb8610b2ae42a12d391709cd 100644
--- a/src/main/resources/plugin.yml
+++ b/src/main/resources/plugin.yml
@@ -1,7 +1,7 @@
 name: SLCraftPlugin
 version: '${project.version}'
 main: com.slprojects.slcraftplugin.Main
-depend: [PlaceholderAPI]
+depend: [ PlaceholderAPI ]
 api-version: 1.18
 authors: [ SofianeLasri ]
 description: Plugin principal de SL-Craft
@@ -9,13 +9,13 @@ website: https://sl-projects.com
 commands:
   wild:
     description: Te permet de te téléporter à une coordonnée aléatoire
-    aliases: [wild, tpr, tprandom]
+    aliases: [ wild, tpr, tprandom ]
     usage: /wild
     permission: slcraft.wild
 
   reset-wild:
     description: ADMIN - Permet de réinitialiser le compteur de téléportation aléatoire d'un joueur
-    aliases: [reset-wild, wildreset]
+    aliases: [ reset-wild, wildreset ]
     usage: /reset-wild
     permission: slcraft.admin.reset-wild