From 09d770ff4fce28bd452e1c89fbae79e81204d371 Mon Sep 17 00:00:00 2001
From: Aria Sangarin <ariahyrule@gmail.com>
Date: Mon, 9 Mar 2020 15:31:57 +0100
Subject: [PATCH] MySQL Update (1.2.4)!

Please reload your config.yml for the options to connect to a database.
---
 pom.xml                                       |   7 +-
 .../java/net/Indyuce/mmocore/MMOCore.java     |  45 ++--
 .../mmocore/api/data/DataProvider.java        |   7 +-
 .../mmocore/api/data/MySQLDataProvider.java   |  29 +++
 .../mmocore/api/data/YAMLDataProvider.java    |  17 +-
 .../mmocore/api/player/OfflinePlayerData.java |   2 +-
 .../mmocore/api/player/PlayerData.java        |   4 +-
 .../mmocore/api/player/PlayerQuests.java      |  45 +++-
 .../mmocore/api/player/Professions.java       |  40 ++-
 .../player/attribute/PlayerAttributes.java    |  30 +++
 .../player/profess/SavedClassInformation.java |  26 +-
 .../api/player/social/guilds/Guild.java       |  15 +-
 .../net/Indyuce/mmocore/api/util/MMOSQL.java  | 150 +++++++++++
 .../Indyuce/mmocore/command/GuildCommand.java |   2 +-
 .../mythicmobs/MythicMobsEnableListener.java  |  26 ++
 .../social/guild/EditableGuildCreation.java   |  14 +-
 .../gui/social/guild/EditableGuildView.java   |   2 +-
 .../mmocore/listener/GuildListener.java       |   4 +-
 .../mmocore/listener/PlayerListener.java      |   2 +-
 .../GuildDataManager.java}                    | 244 +++++++++---------
 .../manager/data/MySQLGuildDataManager.java   |  48 ++++
 .../manager/data/MySQLOfflinePlayerData.java  |  74 ++++++
 .../manager/data/MySQLPlayerDataManager.java  | 150 +++++++++++
 .../manager/data/PlayerDataManager.java       |   5 +-
 .../manager/data/YAMLGuildDataManager.java    |  48 ++++
 .../data/YAMLOfflinePlayerData.java}          |   7 +-
 .../manager/data/YAMLPlayerDataManager.java   |   8 +-
 src/main/resources/config.yml                 |  10 +
 28 files changed, 855 insertions(+), 206 deletions(-)
 create mode 100644 src/main/java/net/Indyuce/mmocore/api/data/MySQLDataProvider.java
 create mode 100644 src/main/java/net/Indyuce/mmocore/api/util/MMOSQL.java
 create mode 100644 src/main/java/net/Indyuce/mmocore/comp/mythicmobs/MythicMobsEnableListener.java
 rename src/main/java/net/Indyuce/mmocore/manager/{social/GuildManager.java => data/GuildDataManager.java} (58%)
 create mode 100644 src/main/java/net/Indyuce/mmocore/manager/data/MySQLGuildDataManager.java
 create mode 100644 src/main/java/net/Indyuce/mmocore/manager/data/MySQLOfflinePlayerData.java
 create mode 100644 src/main/java/net/Indyuce/mmocore/manager/data/MySQLPlayerDataManager.java
 create mode 100644 src/main/java/net/Indyuce/mmocore/manager/data/YAMLGuildDataManager.java
 rename src/main/java/net/Indyuce/mmocore/{api/player/SimpleOfflinePlayerData.java => manager/data/YAMLOfflinePlayerData.java} (82%)

diff --git a/pom.xml b/pom.xml
index c814d376..87d8bc9a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2,7 +2,7 @@
   <modelVersion>4.0.0</modelVersion>
   <groupId>net.Indyuce</groupId>
   <artifactId>MMOCore</artifactId>
-  <version>1.2.3</version>
+  <version>1.2.4</version>
   <description>Offer your players a brand new RPG experience.</description>
   
   <properties>
@@ -159,5 +159,10 @@
         <version>2.10.3</version>
         <scope>provided</scope>
       </dependency>
+      <dependency>
+  		<groupId>mysql</groupId>
+  		<artifactId>mysql-connector-java</artifactId>
+  		<version>5.1.41</version>
+	  </dependency>
     </dependencies>
 </project>
\ No newline at end of file
diff --git a/src/main/java/net/Indyuce/mmocore/MMOCore.java b/src/main/java/net/Indyuce/mmocore/MMOCore.java
index 417f21c3..3502c28a 100644
--- a/src/main/java/net/Indyuce/mmocore/MMOCore.java
+++ b/src/main/java/net/Indyuce/mmocore/MMOCore.java
@@ -13,9 +13,15 @@ import org.bukkit.scheduler.BukkitRunnable;
 
 import net.Indyuce.mmocore.api.ConfigFile;
 import net.Indyuce.mmocore.api.PlayerActionBar;
+import net.Indyuce.mmocore.api.data.DataProvider;
+import net.Indyuce.mmocore.api.data.MySQLDataProvider;
+import net.Indyuce.mmocore.api.data.YAMLDataProvider;
 import net.Indyuce.mmocore.api.player.PlayerData;
 import net.Indyuce.mmocore.api.player.profess.resource.PlayerResource;
+import net.Indyuce.mmocore.api.player.social.guilds.Guild;
 import net.Indyuce.mmocore.api.player.stats.StatType;
+import net.Indyuce.mmocore.api.util.MMOSQL;
+import net.Indyuce.mmocore.api.util.MMOSQL.MySQLConfig;
 import net.Indyuce.mmocore.api.util.debug.DebugMode;
 import net.Indyuce.mmocore.command.AttributesCommand;
 import net.Indyuce.mmocore.command.ClassCommand;
@@ -37,7 +43,7 @@ import net.Indyuce.mmocore.comp.holograms.CMIPlugin;
 import net.Indyuce.mmocore.comp.holograms.HologramSupport;
 import net.Indyuce.mmocore.comp.holograms.HologramsPlugin;
 import net.Indyuce.mmocore.comp.holograms.HolographicDisplaysPlugin;
-import net.Indyuce.mmocore.comp.mythicmobs.MythicMobsDrops;
+import net.Indyuce.mmocore.comp.mythicmobs.MythicMobsEnableListener;
 import net.Indyuce.mmocore.comp.mythicmobs.MythicMobsMMOLoader;
 import net.Indyuce.mmocore.comp.placeholder.DefaultParser;
 import net.Indyuce.mmocore.comp.placeholder.PlaceholderAPIParser;
@@ -78,15 +84,12 @@ import net.Indyuce.mmocore.manager.QuestManager;
 import net.Indyuce.mmocore.manager.RestrictionManager;
 import net.Indyuce.mmocore.manager.SkillManager;
 import net.Indyuce.mmocore.manager.WaypointManager;
-import net.Indyuce.mmocore.manager.data.PlayerDataManager;
-import net.Indyuce.mmocore.manager.data.YAMLPlayerDataManager;
 import net.Indyuce.mmocore.manager.profession.AlchemyManager;
 import net.Indyuce.mmocore.manager.profession.EnchantManager;
 import net.Indyuce.mmocore.manager.profession.FishingManager;
 import net.Indyuce.mmocore.manager.profession.ProfessionManager;
 import net.Indyuce.mmocore.manager.profession.SmithingManager;
 import net.Indyuce.mmocore.manager.social.BoosterManager;
-import net.Indyuce.mmocore.manager.social.GuildManager;
 import net.Indyuce.mmocore.manager.social.PartyManager;
 import net.Indyuce.mmocore.manager.social.RequestManager;
 import net.mmogroup.mmolib.api.stat.StatMap;
@@ -97,13 +100,12 @@ import net.mmogroup.mmolib.version.SpigotPlugin;
 
 public class MMOCore extends JavaPlugin {
 	public static MMOCore plugin;
-
+	
 	public ConfigManager configManager;
 	public WaypointManager waypointManager;
 	public RestrictionManager restrictionManager;
 	public LootableChestManager chestManager;
 	public RequestManager requestManager;
-	public GuildManager guildManager = new GuildManager();
 	public ConfigItemManager configItems;
 	public SkillManager skillManager;
 	public final ClassManager classManager = new ClassManager();
@@ -121,7 +123,7 @@ public class MMOCore extends JavaPlugin {
 	public InventoryManager inventoryManager;
 	public RegionHandler regionHandler;
 	public PlayerActionBar actionBarManager;
-	public final PlayerDataManager playerDataManager = new YAMLPlayerDataManager();
+	public DataProvider dataProvider = new YAMLDataProvider();
 
 	/*
 	 * professions
@@ -135,7 +137,7 @@ public class MMOCore extends JavaPlugin {
 	public RPGUtilHandler rpgUtilHandler = new DefaultRPGUtilHandler();
 
 	private boolean miLoaded, miChecked;
-
+	
 	public void onLoad() {
 		plugin = this;
 
@@ -174,10 +176,11 @@ public class MMOCore extends JavaPlugin {
 			}
 		});
 
-		if (Bukkit.getPluginManager().getPlugin("MythicMobs") != null) {
-			Bukkit.getServer().getPluginManager().registerEvents(new MythicMobsDrops(), this);
-			getLogger().log(Level.INFO, "Hooked onto MythicMobs");
-		}
+		if(getConfig().contains("mysql") && getConfig().getBoolean("mysql.enabled") &&
+			MMOSQL.testConnection(new MySQLConfig(getConfig().getConfigurationSection("mysql"))))
+			dataProvider = new MySQLDataProvider();
+				
+		Bukkit.getPluginManager().registerEvents(new MythicMobsEnableListener(), this);
 
 		if (Bukkit.getPluginManager().getPlugin("Vault") != null)
 			economy = new VaultEconomy();
@@ -292,7 +295,7 @@ public class MMOCore extends JavaPlugin {
 		 * the player datas can't recognize what profess the player has and
 		 * professes will be lost
 		 */
-		Bukkit.getOnlinePlayers().forEach(player -> playerDataManager.setup(player));
+		Bukkit.getOnlinePlayers().forEach(player -> dataProvider.getDataManager().setup(player));
 
 		// commands
 		try {
@@ -341,21 +344,24 @@ public class MMOCore extends JavaPlugin {
 			new BukkitRunnable() {
 				public void run() {
 					for (PlayerData loaded : PlayerData.getAll())
-						playerDataManager.saveData(loaded);
+						dataProvider.getDataManager().saveData(loaded);
 
-					guildManager.save();
+					for (Guild guild : dataProvider.getGuildManager().getAll())
+						dataProvider.getGuildManager().save(guild);
 				}
 			}.runTaskTimerAsynchronously(MMOCore.plugin, autosave, autosave);
 		}
 	}
 
-	public void onDisable() {
+	public void onDisable() {		
 		for (PlayerData data : PlayerData.getAll()) {
 			data.getQuestData().resetBossBar();
-			playerDataManager.saveData(data);
+			dataProvider.getDataManager().saveData(data);
 		}
 
-		guildManager.save();
+		for (Guild guild : dataProvider.getGuildManager().getAll())
+			dataProvider.getGuildManager().save(guild);
+		MMOSQL.stop();
 
 		mineManager.resetRemainingBlocks();
 	}
@@ -374,9 +380,6 @@ public class MMOCore extends JavaPlugin {
 		partyManager.clear();
 		partyManager.reload();
 
-		guildManager.clear();
-		guildManager.reload();
-
 		attributeManager.clear();
 		attributeManager.reload();
 
diff --git a/src/main/java/net/Indyuce/mmocore/api/data/DataProvider.java b/src/main/java/net/Indyuce/mmocore/api/data/DataProvider.java
index be425306..adfaa9e1 100644
--- a/src/main/java/net/Indyuce/mmocore/api/data/DataProvider.java
+++ b/src/main/java/net/Indyuce/mmocore/api/data/DataProvider.java
@@ -1,7 +1,7 @@
 package net.Indyuce.mmocore.api.data;
 
+import net.Indyuce.mmocore.manager.data.GuildDataManager;
 import net.Indyuce.mmocore.manager.data.PlayerDataManager;
-import net.Indyuce.mmocore.manager.social.GuildManager;
 
 public interface DataProvider {
 
@@ -12,7 +12,6 @@ public interface DataProvider {
 	 * data
 	 */
 
-	PlayerDataManager provideDataManager();
-
-	GuildManager provideGuildManager();
+	PlayerDataManager getDataManager();
+	GuildDataManager getGuildManager();
 }
diff --git a/src/main/java/net/Indyuce/mmocore/api/data/MySQLDataProvider.java b/src/main/java/net/Indyuce/mmocore/api/data/MySQLDataProvider.java
new file mode 100644
index 00000000..a2fb8c73
--- /dev/null
+++ b/src/main/java/net/Indyuce/mmocore/api/data/MySQLDataProvider.java
@@ -0,0 +1,29 @@
+package net.Indyuce.mmocore.api.data;
+
+import net.Indyuce.mmocore.api.util.MMOSQL;
+import net.Indyuce.mmocore.manager.data.GuildDataManager;
+import net.Indyuce.mmocore.manager.data.MySQLPlayerDataManager;
+import net.Indyuce.mmocore.manager.data.PlayerDataManager;
+import net.Indyuce.mmocore.manager.data.YAMLGuildDataManager;
+
+public class MySQLDataProvider implements DataProvider {
+	private final MySQLPlayerDataManager playerManager;
+	private final YAMLGuildDataManager guildManager;
+	
+	public MySQLDataProvider() {
+		playerManager = new MySQLPlayerDataManager();
+		guildManager = new YAMLGuildDataManager();
+		
+		MMOSQL.createTables();
+	}
+	
+	@Override
+	public PlayerDataManager getDataManager() {
+		return playerManager;
+	}
+
+	@Override
+	public GuildDataManager getGuildManager() {
+		return guildManager;
+	}
+}
diff --git a/src/main/java/net/Indyuce/mmocore/api/data/YAMLDataProvider.java b/src/main/java/net/Indyuce/mmocore/api/data/YAMLDataProvider.java
index db7106e0..63c8963d 100644
--- a/src/main/java/net/Indyuce/mmocore/api/data/YAMLDataProvider.java
+++ b/src/main/java/net/Indyuce/mmocore/api/data/YAMLDataProvider.java
@@ -1,18 +1,25 @@
 package net.Indyuce.mmocore.api.data;
 
+import net.Indyuce.mmocore.manager.data.GuildDataManager;
 import net.Indyuce.mmocore.manager.data.PlayerDataManager;
+import net.Indyuce.mmocore.manager.data.YAMLGuildDataManager;
 import net.Indyuce.mmocore.manager.data.YAMLPlayerDataManager;
-import net.Indyuce.mmocore.manager.social.GuildManager;
 
 public class YAMLDataProvider implements DataProvider {
+	private final YAMLPlayerDataManager playerManager;
+	private final YAMLGuildDataManager guildManager;
+	public YAMLDataProvider() {
+		playerManager = new YAMLPlayerDataManager();
+		guildManager = new YAMLGuildDataManager();
+	}
 	
 	@Override
-	public PlayerDataManager provideDataManager() {
-		return new YAMLPlayerDataManager();
+	public PlayerDataManager getDataManager() {
+		return playerManager;
 	}
 
 	@Override
-	public GuildManager provideGuildManager() {
-		return new GuildManager();
+	public GuildDataManager getGuildManager() {
+		return guildManager;
 	}
 }
diff --git a/src/main/java/net/Indyuce/mmocore/api/player/OfflinePlayerData.java b/src/main/java/net/Indyuce/mmocore/api/player/OfflinePlayerData.java
index 283f9be8..308b464a 100644
--- a/src/main/java/net/Indyuce/mmocore/api/player/OfflinePlayerData.java
+++ b/src/main/java/net/Indyuce/mmocore/api/player/OfflinePlayerData.java
@@ -27,6 +27,6 @@ public abstract class OfflinePlayerData {
 	public abstract long getLastLogin();
 
 	public static OfflinePlayerData get(UUID uuid) {
-		return MMOCore.plugin.playerDataManager.getOffline(uuid);
+		return MMOCore.plugin.dataProvider.getDataManager().getOffline(uuid);
 	}
 }
diff --git a/src/main/java/net/Indyuce/mmocore/api/player/PlayerData.java b/src/main/java/net/Indyuce/mmocore/api/player/PlayerData.java
index 997b43fe..f42d0019 100644
--- a/src/main/java/net/Indyuce/mmocore/api/player/PlayerData.java
+++ b/src/main/java/net/Indyuce/mmocore/api/player/PlayerData.java
@@ -123,11 +123,11 @@ public class PlayerData extends OfflinePlayerData {
 	}
 
 	public static PlayerData get(UUID uuid) {
-		return MMOCore.plugin.playerDataManager.get(uuid);
+		return MMOCore.plugin.dataProvider.getDataManager().get(uuid);
 	}
 
 	public static Collection<PlayerData> getAll() {
-		return MMOCore.plugin.playerDataManager.getLoaded();
+		return MMOCore.plugin.dataProvider.getDataManager().getLoaded();
 	}
 
 	public PlayerData setPlayer(Player player) {
diff --git a/src/main/java/net/Indyuce/mmocore/api/player/PlayerQuests.java b/src/main/java/net/Indyuce/mmocore/api/player/PlayerQuests.java
index b489da61..40ed0960 100644
--- a/src/main/java/net/Indyuce/mmocore/api/player/PlayerQuests.java
+++ b/src/main/java/net/Indyuce/mmocore/api/player/PlayerQuests.java
@@ -3,6 +3,7 @@ package net.Indyuce.mmocore.api.player;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
 import java.util.logging.Level;
 
@@ -13,6 +14,10 @@ import org.bukkit.boss.BarStyle;
 import org.bukkit.boss.BossBar;
 import org.bukkit.configuration.ConfigurationSection;
 
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+
 import net.Indyuce.mmocore.MMOCore;
 import net.Indyuce.mmocore.api.quest.Quest;
 import net.Indyuce.mmocore.api.quest.QuestProgress;
@@ -36,7 +41,7 @@ public class PlayerQuests {
 			try {
 				current = MMOCore.plugin.questManager.get(config.getString("current.id")).generateNewProgress(playerData, config.getInt("current.objective"));
 			} catch (Exception e) {
-				playerData.log(Level.WARNING, "Couldn't load current quest progress (ID '" + config.getString("current.id") + "'");
+				playerData.log(Level.WARNING, "Couldn't load current quest progress (ID '" + config.getString("current.id") + "')");
 			}
 
 		if (config.contains("finished"))
@@ -64,6 +69,44 @@ public class PlayerQuests {
 			config.set("finished." + key, finished.get(key));
 	}
 
+	public String toJsonString() {
+		JsonObject json = new JsonObject();
+		if(current != null) {
+			JsonObject cur = new JsonObject();
+			cur.addProperty("id", current.getQuest().getId());
+			cur.addProperty("objective", current.getObjectiveNumber());
+			json.add("current", cur);
+		}
+		
+		JsonObject fin = new JsonObject();
+		for (String key : finished.keySet())
+			fin.addProperty(key, finished.get(key));
+		
+		if(finished.size() != 0)
+			json.add("finished", fin);
+		return json.toString();
+	}
+
+	public void load(String json) {
+		Gson parser = new Gson();
+		JsonObject jo = parser.fromJson(json, JsonObject.class);
+		if(jo.has("current")) {
+			JsonObject cur = jo.getAsJsonObject("current");
+			try {
+				current = MMOCore.plugin.questManager.get(cur.get("id").getAsString()).generateNewProgress(playerData, cur.get("objective").getAsInt());
+			} catch (Exception e) {
+				playerData.log(Level.WARNING, "Couldn't load current quest progress (ID '" + cur.get("id").getAsString() + "')");
+			}
+		}
+		if(jo.has("finished")) {
+			for (Entry<String, JsonElement> entry : jo.getAsJsonObject("finished").entrySet())
+				finished.put(entry.getKey(), entry.getValue().getAsLong());
+		}
+		
+		for(Entry<String, Long> entry : finished.entrySet())
+			MMOCore.log("Finished: (" + entry.getKey() + ") - at: " + entry.getValue());
+	}
+	
 	public QuestProgress getCurrent() {
 		return current;
 	}
diff --git a/src/main/java/net/Indyuce/mmocore/api/player/Professions.java b/src/main/java/net/Indyuce/mmocore/api/player/Professions.java
index edcb3ff0..d6bed132 100644
--- a/src/main/java/net/Indyuce/mmocore/api/player/Professions.java
+++ b/src/main/java/net/Indyuce/mmocore/api/player/Professions.java
@@ -2,6 +2,7 @@ package net.Indyuce.mmocore.api.player;
 
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Map.Entry;
 
 import org.bukkit.Bukkit;
 import org.bukkit.ChatColor;
@@ -10,6 +11,10 @@ import org.bukkit.Particle;
 import org.bukkit.Sound;
 import org.bukkit.configuration.ConfigurationSection;
 
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+
 import net.Indyuce.mmocore.MMOCore;
 import net.Indyuce.mmocore.api.ConfigMessage;
 import net.Indyuce.mmocore.api.event.PlayerLevelUpEvent;
@@ -42,6 +47,29 @@ public class Professions {
 			config.set(id + ".level", level.get(id));
 	}
 
+	public String toJsonString() {
+		JsonObject json = new JsonObject();
+		for (Profession prof : MMOCore.plugin.professionManager.getAll()) {
+			JsonObject p = new JsonObject();
+			p.addProperty("exp", getExperience(prof));
+			p.addProperty("level", getLevel(prof));
+
+			json.add(prof.getId(), p);
+		}
+		return json.toString();
+	}
+
+	public void load(String json) {
+		Gson parser = new Gson();
+		JsonObject jo = parser.fromJson(json, JsonObject.class);
+		for (Entry<String, JsonElement> entry : jo.entrySet()) {
+			if (MMOCore.plugin.professionManager.has(entry.getKey())) {
+				exp.put(entry.getKey(), entry.getValue().getAsJsonObject().get("exp").getAsInt());
+				level.put(entry.getKey(), entry.getValue().getAsJsonObject().get("level").getAsInt());
+			}
+		}
+	}
+
 	public PlayerData getPlayerData() {
 		return playerData;
 	}
@@ -54,22 +82,22 @@ public class Professions {
 		return getLevel(profession.getId());
 	}
 
-	public int getExperience(Profession profession) {
-		return getExperience(profession.getId());
-	}
-
 	public int getExperience(String id) {
 		return exp.containsKey(id) ? exp.get(id) : 0;
 	}
 
-	public void setExperience(Profession profession, int value) {
-		exp.put(profession.getId(), value);
+	public int getExperience(Profession profession) {
+		return getExperience(profession.getId());
 	}
 
 	public void setLevel(Profession profession, int value) {
 		level.put(profession.getId(), value);
 	}
 
+	public void setExperience(Profession profession, int value) {
+		exp.put(profession.getId(), value);
+	}
+
 	public void giveLevels(Profession profession, int value) {
 		int total = 0, level = getLevel(profession);
 		while (value-- > 0)
diff --git a/src/main/java/net/Indyuce/mmocore/api/player/attribute/PlayerAttributes.java b/src/main/java/net/Indyuce/mmocore/api/player/attribute/PlayerAttributes.java
index ac81a5cc..035a270e 100644
--- a/src/main/java/net/Indyuce/mmocore/api/player/attribute/PlayerAttributes.java
+++ b/src/main/java/net/Indyuce/mmocore/api/player/attribute/PlayerAttributes.java
@@ -3,12 +3,17 @@ package net.Indyuce.mmocore.api.player.attribute;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
 import java.util.logging.Level;
 
 import org.apache.commons.lang.Validate;
 import org.bukkit.configuration.ConfigurationSection;
 
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+
 import net.Indyuce.mmocore.MMOCore;
 import net.Indyuce.mmocore.api.player.PlayerData;
 import net.mmogroup.mmolib.api.stat.modifier.StatModifier;
@@ -40,6 +45,31 @@ public class PlayerAttributes {
 		extra.values().forEach(ins -> config.set(ins.id, ins.getBase()));
 	}
 
+	public String toJsonString() {
+		JsonObject json = new JsonObject();
+		for (AttributeInstance ins : extra.values())
+			json.addProperty(ins.getId(), ins.getBase());
+		return json.toString();
+	}
+
+	public void load(String json) {
+		Gson parser = new Gson();
+		JsonObject jo = parser.fromJson(json, JsonObject.class);
+		for (Entry<String, JsonElement> entry : jo.entrySet()) {
+			try {
+				String id = entry.getKey().toLowerCase().replace("_", "-").replace(" ", "-");
+				Validate.isTrue(MMOCore.plugin.attributeManager.has(id), "Could not find attribute '" + id + "'");
+
+				PlayerAttribute attribute = MMOCore.plugin.attributeManager.get(id);
+				AttributeInstance ins = new AttributeInstance(attribute);
+				ins.setBase(entry.getValue().getAsInt());
+				extra.put(id, ins);
+			} catch (IllegalArgumentException exception) {
+				data.log(Level.WARNING, exception.getMessage());
+			}
+		}
+	}
+
 	public PlayerData getData() {
 		return data;
 	}
diff --git a/src/main/java/net/Indyuce/mmocore/api/player/profess/SavedClassInformation.java b/src/main/java/net/Indyuce/mmocore/api/player/profess/SavedClassInformation.java
index 81ca09b4..25064e54 100644
--- a/src/main/java/net/Indyuce/mmocore/api/player/profess/SavedClassInformation.java
+++ b/src/main/java/net/Indyuce/mmocore/api/player/profess/SavedClassInformation.java
@@ -2,10 +2,14 @@ package net.Indyuce.mmocore.api.player.profess;
 
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
 
 import org.bukkit.configuration.ConfigurationSection;
 
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+
 import net.Indyuce.mmocore.api.player.PlayerData;
 import net.Indyuce.mmocore.api.player.attribute.PlayerAttribute;
 import net.Indyuce.mmocore.api.player.profess.PlayerClass.ClassOption;
@@ -25,12 +29,30 @@ public class SavedClassInformation {
 
 		attributes = new HashMap<>();
 		if (config.contains("attribute"))
-			config.getKeys(false).forEach(key -> attributes.put(key, config.getInt(key)));
+			config.getConfigurationSection("attribute").getKeys(false).forEach(key -> attributes.put(key, config.getInt(key)));
 		skills = new HashMap<>();
 		if (config.contains("skill"))
-			config.getKeys(false).forEach(key -> skills.put(key, config.getInt(key)));
+			config.getConfigurationSection("skill").getKeys(false).forEach(key -> skills.put(key, config.getInt(key)));
 	}
 
+	public SavedClassInformation(JsonObject json) {
+		level = json.get("level").getAsInt();
+		experience = json.get("experience").getAsInt();
+		skillPoints = json.get("skill-points").getAsInt();
+		attributePoints = json.get("attribute-points").getAsInt();
+		attributeReallocationPoints = json.get("attribute-realloc-points").getAsInt();
+
+		attributes = new HashMap<>();
+		if (json.has("attribute"))
+			for(Entry<String, JsonElement> entry : json.getAsJsonObject("attribute").entrySet())
+				attributes.put(entry.getKey(), entry.getValue().getAsInt());
+		skills = new HashMap<>();
+		if (json.has("skill"))
+			for(Entry<String, JsonElement> entry : json.getAsJsonObject("skill").entrySet())
+				skills.put(entry.getKey(), entry.getValue().getAsInt());
+			
+	}
+	
 	public SavedClassInformation(PlayerData player) {
 		level = player.getLevel();
 		skillPoints = player.getSkillPoints();
diff --git a/src/main/java/net/Indyuce/mmocore/api/player/social/guilds/Guild.java b/src/main/java/net/Indyuce/mmocore/api/player/social/guilds/Guild.java
index 6bde032f..d580baee 100644
--- a/src/main/java/net/Indyuce/mmocore/api/player/social/guilds/Guild.java
+++ b/src/main/java/net/Indyuce/mmocore/api/player/social/guilds/Guild.java
@@ -8,7 +8,6 @@ import java.util.UUID;
 import java.util.function.Consumer;
 
 import org.bukkit.Bukkit;
-import org.bukkit.configuration.file.FileConfiguration;
 import org.bukkit.entity.Player;
 
 import net.Indyuce.mmocore.MMOCore;
@@ -81,7 +80,7 @@ public class Guild {
 
 			// disband the guild if no member left
 			if (members.count() < 1) {
-				MMOCore.plugin.guildManager.unregisterGuild(this);
+				MMOCore.plugin.dataProvider.getGuildManager().unregisterGuild(this);
 				return;
 			}
 
@@ -122,18 +121,6 @@ public class Guild {
 		new ConfigMessage("guild-invite").addPlaceholders("player", inviter.getPlayer().getName(), "uuid", request.getUniqueId().toString()).sendAsJSon(target.getPlayer());
 		MMOCore.plugin.requestManager.registerRequest(request);
 	}
-
-	public void saveInConfig(FileConfiguration config) {
-		config.set("name", guildName);
-		config.set("tag", guildTag);
-		config.set("owner", owner.toString());
-		
-		List<String> memberList = new ArrayList<>();
-		for(UUID uuid : members.members)
-			memberList.add(uuid.toString());
-			
-		config.set("members", memberList);
-	}
 	
 	public class GuildMembers {
 		private final List<UUID> members = new ArrayList<>();
diff --git a/src/main/java/net/Indyuce/mmocore/api/util/MMOSQL.java b/src/main/java/net/Indyuce/mmocore/api/util/MMOSQL.java
new file mode 100644
index 00000000..7ef97ccd
--- /dev/null
+++ b/src/main/java/net/Indyuce/mmocore/api/util/MMOSQL.java
@@ -0,0 +1,150 @@
+package net.Indyuce.mmocore.api.util;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.UUID;
+
+import org.bukkit.configuration.ConfigurationSection;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+
+import net.Indyuce.mmocore.MMOCore;
+
+public class MMOSQL {
+	private static Connection connection;
+	
+	public static boolean testConnection(MySQLConfig config) {		
+		try {
+		    Class.forName("com.mysql.jdbc.Driver");
+		} catch (ClassNotFoundException e) {
+		    e.printStackTrace();
+		    System.err.println("jdbc driver unavailable!");
+			return false;
+		}
+		try {
+		    connection = DriverManager.getConnection(config.getConnectionString(), config.getUser(), config.getPassword());
+		    MMOCore.log("Successfully connected to MySQL Database!");
+		} catch (SQLException e) {
+		    e.printStackTrace();
+			return false;
+		}
+		return true;
+	}
+
+	public static void stop() {
+		try {
+	        if(connection != null && !connection.isClosed()) connection.close();
+	    } catch(SQLException e) {
+	        e.printStackTrace();
+	    }
+	}
+	
+	public static void createTables() {
+		executeUpdate("CREATE TABLE IF NOT EXISTS mmocore_playerdata (uuid VARCHAR(36),class_points INT(11) DEFAULT 0,skill_points INT(11) DEFAULT 0,attribute_points INT(11) DEFAULT 0,attribute_realloc_points INT(11) DEFAULT 0,level INT(11) DEFAULT 0,experience INT(11) DEFAULT 0,class VARCHAR(20),guild VARCHAR(20),last_login LONG,attributes JSON,professions JSON,quests JSON,waypoints JSON,friends JSON,skills JSON,bound_skills JSON,class_info JSON,PRIMARY KEY (uuid));");
+		//executeUpdate("CREATE TABLE IF NOT EXISTS mmocore_guilddata(id varchar(10));");
+	}
+
+	public static ResultSet getResult(String sql) {
+		try {
+		    PreparedStatement stmt = connection.prepareStatement(sql);
+			return stmt.executeQuery();
+		} catch (SQLException e) {
+		    e.printStackTrace();
+		    return null;
+		}
+	}
+	
+	public static Collection<String> getJSONArray(String json) {
+		Collection<String> collection = new ArrayList<String>();
+		Gson parser = new Gson();
+		
+		for(String s : parser.fromJson(json, String[].class))
+			collection.add(s);
+		
+		return collection;
+	}
+	
+	private final Table table;
+	private final UUID uuid;
+	
+	public MMOSQL(Table t, UUID player) {
+		table = t;
+		uuid = player; 
+	}
+
+	public void updateData(String key, Object value) {
+		executeUpdate("INSERT INTO " + table + "(uuid, " + key + ") VALUES('" + uuid +
+				"', '" + value + "') ON DUPLICATE KEY UPDATE " + key + "='" + value + "';");
+	}
+	
+	public void updateJSONArray(String key, Collection<String> collection) {
+		JsonArray json = new JsonArray();
+		for(String s : collection)
+			json.add(s);
+		updateData(key, json.toString());
+	}
+	
+	public void updateJSONObject(String key, Set<Entry<String, Integer>> collection) {
+		JsonObject json = new JsonObject();
+		for(Entry<String, Integer> entry : collection)
+			json.addProperty(entry.getKey(), entry.getValue());
+		updateData(key, json.toString());
+	}
+	
+	private static void executeUpdate(String sql) {
+		try {
+		    PreparedStatement stmt = connection.prepareStatement(sql);
+		    stmt.executeUpdate();
+		} catch (SQLException e) {
+		    e.printStackTrace();
+		}
+	}
+	
+	public enum Table {
+		PLAYERDATA("mmocore_playerdata"),
+		GUILDDATA("mmocore_guilddata");
+		
+		final String tableName; 
+		Table(String tN) {
+			tableName = tN;
+		}
+		
+		@Override
+		public String toString() {
+			return tableName;
+		}
+	}
+	
+	public static class MySQLConfig {
+		String database, hostname, userid, password, flags;
+		int port;
+		
+		public MySQLConfig(ConfigurationSection config) {
+			database = config.getString("database", "minecraft");
+			hostname = config.getString("host", "localhost");
+			port = config.getInt("port", 3306);
+			userid = config.getString("user", "mmolover");
+			password = config.getString("pass", "ILoveAria");
+			flags = config.getString("flags", "?allowReconnect=true");
+		}
+
+		public String getConnectionString() {
+			return "jdbc:mysql://" + hostname + ":" + port + "/" + database + flags + "&useSSL=false";
+		}
+		public String getUser() {
+			return userid;
+		}
+		public String getPassword() {
+			return password;
+		}
+	}
+}
diff --git a/src/main/java/net/Indyuce/mmocore/command/GuildCommand.java b/src/main/java/net/Indyuce/mmocore/command/GuildCommand.java
index 1e56db1e..2458a334 100644
--- a/src/main/java/net/Indyuce/mmocore/command/GuildCommand.java
+++ b/src/main/java/net/Indyuce/mmocore/command/GuildCommand.java
@@ -48,7 +48,7 @@ public class GuildCommand extends BukkitCommand {
 				return true;
 			}
 
-			if (!MMOCore.plugin.guildManager.isRegistered(((GuildInvite) request).getGuild())) {
+			if (!MMOCore.plugin.dataProvider.getGuildManager().isRegistered(((GuildInvite) request).getGuild())) {
 				MMOCore.plugin.requestManager.unregisterRequest(uuid);
 				return true;
 			}
diff --git a/src/main/java/net/Indyuce/mmocore/comp/mythicmobs/MythicMobsEnableListener.java b/src/main/java/net/Indyuce/mmocore/comp/mythicmobs/MythicMobsEnableListener.java
new file mode 100644
index 00000000..148aa63f
--- /dev/null
+++ b/src/main/java/net/Indyuce/mmocore/comp/mythicmobs/MythicMobsEnableListener.java
@@ -0,0 +1,26 @@
+package net.Indyuce.mmocore.comp.mythicmobs;
+//package fuck.im.so.dead;
+
+import java.util.logging.Level;
+
+//import plz.dont.hurt.me.Indy;
+import org.bukkit.Bukkit;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.server.PluginEnableEvent;
+
+import net.Indyuce.mmocore.MMOCore;
+//Wooooow
+
+public class MythicMobsEnableListener /**This is for fixing a bug, not just stupid code, plz plz plz*/ implements Listener /**here my lovely indy, it's a bug fix*/ {
+	@EventHandler //For handling this very cool class
+	public void iloveyoupleasedonthurtme(PluginEnableEvent /**See this event is super cool, but I understand why you'd dislike this "fix"*/ imsorryindy) {
+		if (imsorryindy.getPlugin().getName().equals("MythicMobs")) /**The load order issue isn't caused by anyone, but I wish we could have an easier fix*/ {
+			Bukkit.getServer().getPluginManager().registerEvents(new MythicMobsDrops(), MMOCore.plugin);
+			//Can you even safely register an event within a listener and an eventhandler method?? I don't know, but it works for now! 
+			MMOCore.plugin.getLogger().log(Level.INFO, "Hooked onto MythicMobs"); //PLEASE LOG MY DEATH WHEN INDY SLITS MY THROAT, I'M SCARED
+		}
+	}
+}
+
+//Love from Aria
\ No newline at end of file
diff --git a/src/main/java/net/Indyuce/mmocore/gui/social/guild/EditableGuildCreation.java b/src/main/java/net/Indyuce/mmocore/gui/social/guild/EditableGuildCreation.java
index 5b3e83ec..d7afe2a5 100644
--- a/src/main/java/net/Indyuce/mmocore/gui/social/guild/EditableGuildCreation.java
+++ b/src/main/java/net/Indyuce/mmocore/gui/social/guild/EditableGuildCreation.java
@@ -13,7 +13,7 @@ import net.Indyuce.mmocore.gui.api.GeneratedInventory;
 import net.Indyuce.mmocore.gui.api.item.InventoryItem;
 import net.Indyuce.mmocore.gui.api.item.NoPlaceholderItem;
 import net.Indyuce.mmocore.manager.InventoryManager;
-import net.Indyuce.mmocore.manager.social.GuildManager.GuildConfiguration.NamingRules;
+import net.Indyuce.mmocore.manager.data.GuildDataManager.GuildConfiguration.NamingRules;
 
 public class EditableGuildCreation extends EditableInventory {
 	public EditableGuildCreation() {
@@ -41,16 +41,16 @@ public class EditableGuildCreation extends EditableInventory {
 
 			if (item.getFunction().equals("create")) {
 				MMOCore.plugin.configManager.newPlayerInput(player, InputType.GUILD_CREATION_TAG, (input) -> {
-					if(MMOCore.plugin.guildManager.getConfig().shouldUppercaseTags())
+					if(MMOCore.plugin.dataProvider.getGuildManager().getConfig().shouldUppercaseTags())
 						input = input.toUpperCase();
 						
-					if(check(player, input, MMOCore.plugin.guildManager.getConfig().getTagRules())) {
+					if(check(player, input, MMOCore.plugin.dataProvider.getGuildManager().getConfig().getTagRules())) {
 						String tag = input;
 						
 						MMOCore.plugin.configManager.newPlayerInput(player, InputType.GUILD_CREATION_NAME, (name) -> {								
-							if(check(player, name, MMOCore.plugin.guildManager.getConfig().getNameRules())) {
-								MMOCore.plugin.guildManager.newRegisteredGuild(playerData.getUniqueId(), name, tag);
-								MMOCore.plugin.guildManager.getGuild(tag.toLowerCase()).addMember(playerData.getUniqueId());
+							if(check(player, name, MMOCore.plugin.dataProvider.getGuildManager().getConfig().getNameRules())) {
+								MMOCore.plugin.dataProvider.getGuildManager().newRegisteredGuild(playerData.getUniqueId(), name, tag);
+								MMOCore.plugin.dataProvider.getGuildManager().getGuild(tag.toLowerCase()).addMember(playerData.getUniqueId());
 
 								InventoryManager.GUILD_VIEW.newInventory(playerData).open();
 								player.playSound(player.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1, 1);
@@ -77,7 +77,7 @@ public class EditableGuildCreation extends EditableInventory {
 		
 		if(input.length() <= rules.getMax() && input.length() >= rules.getMin())
 			if(input.matches(rules.getRegex()))
-				if(!MMOCore.plugin.guildManager.isRegistered(input))
+				if(!MMOCore.plugin.dataProvider.getGuildManager().isRegistered(input))
 					return true;
 				else
 					reason = MMOCore.plugin.configManager.getSimpleMessage("guild-creation.reasons.already-taken").message();
diff --git a/src/main/java/net/Indyuce/mmocore/gui/social/guild/EditableGuildView.java b/src/main/java/net/Indyuce/mmocore/gui/social/guild/EditableGuildView.java
index c12d21f2..1c1e2f29 100644
--- a/src/main/java/net/Indyuce/mmocore/gui/social/guild/EditableGuildView.java
+++ b/src/main/java/net/Indyuce/mmocore/gui/social/guild/EditableGuildView.java
@@ -178,7 +178,7 @@ public class EditableGuildView extends EditableInventory {
 			if (item.getFunction().equals("disband")) {
 				if (!playerData.getGuild().getOwner().equals(playerData.getUniqueId()))
 					return;
-				MMOCore.plugin.guildManager.unregisterGuild(playerData.getGuild());
+				MMOCore.plugin.dataProvider.getGuildManager().unregisterGuild(playerData.getGuild());
 				player.playSound(player.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1, 1);
 				player.closeInventory();
 				return;
diff --git a/src/main/java/net/Indyuce/mmocore/listener/GuildListener.java b/src/main/java/net/Indyuce/mmocore/listener/GuildListener.java
index 9787481e..c94ce3f0 100644
--- a/src/main/java/net/Indyuce/mmocore/listener/GuildListener.java
+++ b/src/main/java/net/Indyuce/mmocore/listener/GuildListener.java
@@ -15,7 +15,7 @@ import net.Indyuce.mmocore.manager.ConfigManager.SimpleMessage;
 public class GuildListener implements Listener {
 	@EventHandler(priority = EventPriority.LOW)
 	public void a(AsyncPlayerChatEvent event) {
-		if (!event.getMessage().startsWith(MMOCore.plugin.guildManager.getConfig().getPrefix()))
+		if (!event.getMessage().startsWith(MMOCore.plugin.dataProvider.getGuildManager().getConfig().getPrefix()))
 			return;
 
 		PlayerData data = PlayerData.get(event.getPlayer());
@@ -28,7 +28,7 @@ public class GuildListener implements Listener {
 		 * running it in a delayed task is recommended
 		 */
 		Bukkit.getScheduler().scheduleSyncDelayedTask(MMOCore.plugin, () -> {
-			SimpleMessage format = MMOCore.plugin.configManager.getSimpleMessage("guild-chat", "player", data.getPlayer().getName(), "tag", data.getGuild().getTag(), "message", event.getMessage().substring(MMOCore.plugin.guildManager.getConfig().getPrefix().length()));
+			SimpleMessage format = MMOCore.plugin.configManager.getSimpleMessage("guild-chat", "player", data.getPlayer().getName(), "tag", data.getGuild().getTag(), "message", event.getMessage().substring(MMOCore.plugin.dataProvider.getGuildManager().getConfig().getPrefix().length()));
 			GuildChatEvent called = new GuildChatEvent(data, format.message());
 			Bukkit.getPluginManager().callEvent(called);
 			if (!called.isCancelled())
diff --git a/src/main/java/net/Indyuce/mmocore/listener/PlayerListener.java b/src/main/java/net/Indyuce/mmocore/listener/PlayerListener.java
index 2b05f1a3..2d277367 100644
--- a/src/main/java/net/Indyuce/mmocore/listener/PlayerListener.java
+++ b/src/main/java/net/Indyuce/mmocore/listener/PlayerListener.java
@@ -30,7 +30,7 @@ public class PlayerListener implements Listener {
 	@EventHandler(priority = EventPriority.LOW)
 	public void a(PlayerJoinEvent event) {
 		Player player = event.getPlayer();
-		MMOCore.plugin.playerDataManager.setup(player).getStats().getMap().updateAll();
+		MMOCore.plugin.dataProvider.getDataManager().setup(player).getStats().getMap().updateAll();
 	}
 
 	/*
diff --git a/src/main/java/net/Indyuce/mmocore/manager/social/GuildManager.java b/src/main/java/net/Indyuce/mmocore/manager/data/GuildDataManager.java
similarity index 58%
rename from src/main/java/net/Indyuce/mmocore/manager/social/GuildManager.java
rename to src/main/java/net/Indyuce/mmocore/manager/data/GuildDataManager.java
index f05139e1..4e63cc52 100644
--- a/src/main/java/net/Indyuce/mmocore/manager/social/GuildManager.java
+++ b/src/main/java/net/Indyuce/mmocore/manager/data/GuildDataManager.java
@@ -1,129 +1,115 @@
-package net.Indyuce.mmocore.manager.social;
-
-import java.io.File;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.UUID;
-
-import org.bukkit.configuration.ConfigurationSection;
-import org.bukkit.configuration.file.FileConfiguration;
-import org.bukkit.configuration.file.YamlConfiguration;
-
-import net.Indyuce.mmocore.MMOCore;
-import net.Indyuce.mmocore.api.ConfigFile;
-import net.Indyuce.mmocore.api.player.social.guilds.Guild;
-import net.Indyuce.mmocore.manager.MMOManager;
-
-public class GuildManager extends MMOManager {
-	private final Map<String, Guild> guilds = new HashMap<>();
-	private GuildConfiguration config;
-
-	public Guild newRegisteredGuild(UUID owner, String name, String tag) {
-		Guild guild = new Guild(owner, name, tag);
-		registerGuild(guild);
-		return guild;
-	}
-
-	public void registerGuild(Guild guild) {
-		guilds.put(guild.getId(), guild);
-	}
-
-	public boolean isRegistered(Guild guild) {
-		return guilds.containsValue(guild);
-	}
-
-	public boolean isRegistered(String tag) {
-		return guilds.containsKey(tag);
-	}
-	
-	public void unregisterGuild(Guild guild) {
-		guild.getMembers().forEach(member -> guild.removeMember(member, true));
-		guild.getMembers().clear();
-		guilds.remove(guild.getId());
-		
-		new ConfigFile(guild).delete();
-	}
-
-	public void save() {
-		for(Guild guild : guilds.values()) {
-			ConfigFile config = new ConfigFile(guild);
-			guild.saveInConfig(config.getConfig());
-			config.save();
-		}
-	}
-	
-	public GuildConfiguration getConfig()
-	{ return config; }
-
-	@Override
-	public void reload() {
-		config = new GuildConfiguration();
-
-		File guildsFolder = new File(MMOCore.plugin.getDataFolder(), "guilds");
-		if(!guildsFolder.exists()) guildsFolder.mkdirs();
-	    for (File file : guildsFolder.listFiles()) {
-	        if (!file.isDirectory() && file.getName().substring(file.getName().lastIndexOf('.')).equalsIgnoreCase(".yml")) {
-	        	FileConfiguration c = YamlConfiguration.loadConfiguration(file);
-	        	Guild guild = newRegisteredGuild(UUID.fromString(c.getString("owner")), c.getString("name"), c.getString("tag"));
-	        	for(String m : c.getStringList("members"))
-	        		guild.registerMember(UUID.fromString(m));
-	        }
-	    }
-	}
-
-	@Override
-	public void clear() { }
-
-	public Guild getGuild(String guild)
-	{ return guilds.get(guild); }
-	
-	public class GuildConfiguration {
-		private final String prefix;
-		private final boolean uppercaseTags;
-		private final NamingRules tagRules, nameRules;
-		
-		public GuildConfiguration() {
-			FileConfiguration config = new ConfigFile("guilds").getConfig();
-			
-			prefix = config.getString("chat-prefix", "*");
-			uppercaseTags = config.getBoolean("uppercase-tags", true);
-			tagRules = new NamingRules(config.getConfigurationSection("rules.tag"));
-			nameRules = new NamingRules(config.getConfigurationSection("rules.name"));
-		}
-		
-		public String getPrefix()
-		{ return prefix; }
-		public boolean shouldUppercaseTags()
-		{ return uppercaseTags; }
-		public NamingRules getTagRules()
-		{ return tagRules; }
-		public NamingRules getNameRules()
-		{ return nameRules; }
-		
-		public class NamingRules {
-			private final String regex;
-			private final int min, max;
-			
-			public NamingRules(ConfigurationSection config) {
-				regex = config.getString("matches", "[a-zA-Z-_!?]+");
-				min = config.getInt("min-length", 3);
-				max = config.getInt("max-length", 5);
-			}
-			
-			public String getRegex()
-			{ return regex; }
-			public int getMin()
-			{ return min; }
-			public int getMax()
-			{ return max; }
-		}
-	}
-
-	// Used to check if player was kicked while offline
-	public Guild stillInGuild(UUID uuid, String id) {
-		Guild guild = getGuild(id);
-		if(guild != null && guild.getMembers().has(uuid))
-			return guild;
-		return null;
-	}
-}
+package net.Indyuce.mmocore.manager.data;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import org.bukkit.configuration.ConfigurationSection;
+import org.bukkit.configuration.file.FileConfiguration;
+
+import net.Indyuce.mmocore.api.ConfigFile;
+import net.Indyuce.mmocore.api.player.social.guilds.Guild;
+
+public abstract class GuildDataManager {	
+	protected final Map<String, Guild> guilds = new HashMap<>();
+	
+	public Guild newRegisteredGuild(UUID owner, String name, String tag) {
+		Guild guild = new Guild(owner, name, tag);
+		registerGuild(guild);
+		return guild;
+	}
+	
+	public void registerGuild(Guild guild) {
+		guilds.put(guild.getId(), guild);
+	}
+
+	public boolean isRegistered(Guild guild) {
+		return guilds.containsValue(guild);
+	}
+
+	public boolean isRegistered(String tag) {
+		return guilds.containsKey(tag);
+	}
+	
+	public void unregisterGuild(Guild guild) {
+		guild.getMembers().forEach(member -> guild.removeMember(member, true));
+		guild.getMembers().clear();
+		guilds.remove(guild.getId());
+		delete(guild);
+	}
+
+	public Guild stillInGuild(UUID uuid, String id) {
+		Guild guild = getGuild(id);
+		if(guild != null && guild.getMembers().has(uuid))
+			return guild;
+		return null;
+	}
+	
+	public Guild getGuild(String guild)
+	{ return guilds.get(guild); }
+	
+	public Collection<Guild> getAll()
+	{ return guilds.values(); }
+	
+	public void reload() {
+		for(Guild guild : guilds.values())
+			save(guild);
+		guilds.clear();
+		load();
+		config = new GuildConfiguration();
+	}
+	
+	public abstract void save(Guild guild);
+	public abstract void load();
+	public abstract void delete(Guild guild);
+
+	//Shitty code for loading config values for guilds.
+	private GuildConfiguration config;
+	public GuildConfiguration getConfig() {
+		if(config == null)
+			config = new GuildConfiguration();
+		return config;
+	}
+	public class GuildConfiguration {
+		private final String prefix;
+		private final boolean uppercaseTags;
+		private final NamingRules tagRules, nameRules;
+		
+		public GuildConfiguration() {
+			FileConfiguration config = new ConfigFile("guilds").getConfig();
+			
+			this.prefix = config.getString("chat-prefix", "*");
+			this.uppercaseTags = config.getBoolean("uppercase-tags", true);
+			this.tagRules = new NamingRules(config.getConfigurationSection("rules.tag"));
+			this.nameRules = new NamingRules(config.getConfigurationSection("rules.name"));
+		}
+		
+		public String getPrefix()
+		{ return prefix; }
+		public boolean shouldUppercaseTags()
+		{ return uppercaseTags; }
+		public NamingRules getTagRules()
+		{ return tagRules; }
+		public NamingRules getNameRules()
+		{ return nameRules; }
+		
+		public class NamingRules {
+			private final String regex;
+			private final int min, max;
+			
+			public NamingRules(ConfigurationSection config) {
+				regex = config.getString("matches", "[a-zA-Z-_!?]+");
+				min = config.getInt("min-length", 3);
+				max = config.getInt("max-length", 5);
+			}
+			
+			public String getRegex()
+			{ return regex; }
+			public int getMin()
+			{ return min; }
+			public int getMax()
+			{ return max; }
+		}
+	}
+}
diff --git a/src/main/java/net/Indyuce/mmocore/manager/data/MySQLGuildDataManager.java b/src/main/java/net/Indyuce/mmocore/manager/data/MySQLGuildDataManager.java
new file mode 100644
index 00000000..e8ec65fc
--- /dev/null
+++ b/src/main/java/net/Indyuce/mmocore/manager/data/MySQLGuildDataManager.java
@@ -0,0 +1,48 @@
+package net.Indyuce.mmocore.manager.data;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import org.bukkit.configuration.file.FileConfiguration;
+import org.bukkit.configuration.file.YamlConfiguration;
+
+import net.Indyuce.mmocore.MMOCore;
+import net.Indyuce.mmocore.api.ConfigFile;
+import net.Indyuce.mmocore.api.player.social.guilds.Guild;
+
+public class MySQLGuildDataManager extends GuildDataManager {
+	@Override
+	public void save(Guild guild) {
+		ConfigFile config = new ConfigFile(guild);
+		config.getConfig().set("name", guild.getName());
+		config.getConfig().set("tag", guild.getTag());
+		config.getConfig().set("owner", guild.getOwner().toString());
+		
+		List<String> memberList = new ArrayList<>();
+		guild.getMembers().forEach(uuid -> memberList.add(uuid.toString()));
+		config.getConfig().set("members", memberList);
+		
+		config.save();
+	}
+	
+	@Override
+	public void load() {
+		File guildsFolder = new File(MMOCore.plugin.getDataFolder(), "guilds");
+		if(!guildsFolder.exists()) guildsFolder.mkdirs();
+	    for (File file : guildsFolder.listFiles()) {
+	        if (!file.isDirectory() && file.getName().substring(file.getName().lastIndexOf('.')).equalsIgnoreCase(".yml")) {
+	        	FileConfiguration c = YamlConfiguration.loadConfiguration(file);
+	        	Guild guild = newRegisteredGuild(UUID.fromString(c.getString("owner")), c.getString("name"), c.getString("tag"));
+	        	for(String m : c.getStringList("members"))
+	        		guild.registerMember(UUID.fromString(m));
+	        }
+	    }
+	}
+
+	@Override
+	public void delete(Guild guild) {
+		new ConfigFile(guild).delete();
+	}
+}
diff --git a/src/main/java/net/Indyuce/mmocore/manager/data/MySQLOfflinePlayerData.java b/src/main/java/net/Indyuce/mmocore/manager/data/MySQLOfflinePlayerData.java
new file mode 100644
index 00000000..ea507891
--- /dev/null
+++ b/src/main/java/net/Indyuce/mmocore/manager/data/MySQLOfflinePlayerData.java
@@ -0,0 +1,74 @@
+package net.Indyuce.mmocore.manager.data;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+import net.Indyuce.mmocore.MMOCore;
+import net.Indyuce.mmocore.api.player.OfflinePlayerData;
+import net.Indyuce.mmocore.api.player.profess.PlayerClass;
+import net.Indyuce.mmocore.api.util.MMOSQL;
+import net.Indyuce.mmocore.api.util.MMOSQL.Table;
+
+public class MySQLOfflinePlayerData extends OfflinePlayerData {
+	int level;
+	long lastLogin;
+	PlayerClass profess;
+	List<UUID> friends;
+	
+	public MySQLOfflinePlayerData(UUID uuid) {
+		super(uuid);
+		
+		ResultSet result = MMOSQL.getResult("SELECT * FROM mmocore_playerdata WHERE uuid = '" + uuid + "';");
+
+		try {
+			if(!result.next()) {
+				level = 0;
+				lastLogin = 0;
+				profess = MMOCore.plugin.classManager.getDefaultClass();
+				friends = new ArrayList<UUID>();
+			}
+			else while(result.next()) {
+				level = result.getInt("level");
+				lastLogin = result.getLong("last_login");
+				profess = result.getString("class").equalsIgnoreCase("null") ? MMOCore.plugin.classManager.getDefaultClass()
+						: MMOCore.plugin.classManager.get(result.getString("class"));
+				if(!result.getString("friends").equalsIgnoreCase("null"))
+					MMOSQL.getJSONArray(result.getString("friends")).forEach(str -> friends.add(UUID.fromString(str)));
+				else friends = new ArrayList<UUID>();
+			}
+		} catch (SQLException e) {
+			e.printStackTrace();
+		}
+	}
+
+	@Override
+	public void removeFriend(UUID uuid) {
+		friends.remove(uuid);
+		new MMOSQL(Table.PLAYERDATA, uuid).updateData("friends",
+			friends.stream().map(friend -> friend.toString()).collect(Collectors.toList()));
+	}
+
+	@Override
+	public boolean hasFriend(UUID uuid) {
+		return friends.contains(uuid);
+	}
+
+	@Override
+	public PlayerClass getProfess() {
+		return profess; 
+	}
+
+	@Override
+	public int getLevel() {
+		return level;
+	}
+
+	@Override
+	public long getLastLogin() {
+		return lastLogin;
+	}
+}
diff --git a/src/main/java/net/Indyuce/mmocore/manager/data/MySQLPlayerDataManager.java b/src/main/java/net/Indyuce/mmocore/manager/data/MySQLPlayerDataManager.java
new file mode 100644
index 00000000..68b50d7b
--- /dev/null
+++ b/src/main/java/net/Indyuce/mmocore/manager/data/MySQLPlayerDataManager.java
@@ -0,0 +1,150 @@
+package net.Indyuce.mmocore.manager.data;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Map.Entry;
+import java.util.UUID;
+import java.util.logging.Level;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang.Validate;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+
+import net.Indyuce.mmocore.MMOCore;
+import net.Indyuce.mmocore.api.player.OfflinePlayerData;
+import net.Indyuce.mmocore.api.player.PlayerData;
+import net.Indyuce.mmocore.api.player.profess.PlayerClass;
+import net.Indyuce.mmocore.api.player.profess.SavedClassInformation;
+import net.Indyuce.mmocore.api.player.stats.StatType;
+import net.Indyuce.mmocore.api.util.MMOSQL;
+import net.Indyuce.mmocore.api.util.MMOSQL.Table;
+
+public class MySQLPlayerDataManager extends PlayerDataManager {
+	@Override
+	public void loadData(PlayerData data) {
+		ResultSet result = MMOSQL.getResult("SELECT * FROM mmocore_playerdata WHERE uuid = '" + data.getUniqueId() + "';");
+		if(result == null)
+			MMOCore.log(Level.SEVERE, "Failed to load playerdata from MySQL!");
+		try {
+			if(!result.next()) {
+				return;
+			}
+			Gson parser = new Gson();
+			
+			data.setClassPoints(result.getInt("class_points"));
+			data.setSkillPoints(result.getInt("skill_points"));
+			data.setAttributePoints(result.getInt("attribute_points"));
+			data.setAttributeReallocationPoints(result.getInt("attribute_realloc_points"));
+			data.setLevel(result.getInt("level"));
+			data.setExperience(result.getInt("experience"));
+			if(!isEmpty(result.getString("class")))
+				data.setProfess(MMOCore.plugin.classManager.get(result.getString("class")));
+			data.setMana(data.getStats().getStat(StatType.MAX_MANA));
+			data.setStamina(data.getStats().getStat(StatType.MAX_STAMINA));
+			data.setStellium(data.getStats().getStat(StatType.MAX_STELLIUM));
+			if(!isEmpty(result.getString("guild")))
+				data.setGuild(MMOCore.plugin.dataProvider.getGuildManager().stillInGuild(data.getUniqueId(), result.getString("guild")));
+			if(!isEmpty(result.getString("attributes")))
+				data.getAttributes().load(result.getString("attributes"));
+			if(!isEmpty(result.getString("professions")))
+				data.getCollectionSkills().load(result.getString("professions"));
+			String quests = result.getString("quests");
+			if(!isEmpty(quests)) 
+				data.getQuestData().load(quests);
+			data.getQuestData().updateBossBar();
+			if(!isEmpty(result.getString("waypoints")))
+				data.getWaypoints().addAll(MMOSQL.getJSONArray(result.getString("waypoints")));
+			if(!isEmpty(result.getString("friends")))
+				MMOSQL.getJSONArray(result.getString("friends")).forEach(str -> data.getFriends().add(UUID.fromString(str)));
+			if(!isEmpty(result.getString("skills"))) {
+				JsonObject object = parser.fromJson(result.getString("skills"), JsonObject.class);
+				for(Entry<String, JsonElement> entry : object.entrySet())
+					data.setSkillLevel(entry.getKey(), entry.getValue().getAsInt());
+			}
+			if(!isEmpty(result.getString("bound_skills")))
+				for(String skill : MMOSQL.getJSONArray(result.getString("bound_skills")))
+					if(MMOCore.plugin.skillManager.has(skill))
+						data.getBoundSkills().add(data.getProfess().getSkill(skill));
+			if(!isEmpty(result.getString("class_info"))) {
+				JsonObject object = parser.fromJson(result.getString("class_info"), JsonObject.class);
+				for(Entry<String, JsonElement> entry : object.entrySet()) {
+					try {
+						PlayerClass profess = MMOCore.plugin.classManager.get(entry.getKey());
+						Validate.notNull(profess, "Could not find class '" + entry.getKey() + "'");
+						data.applyClassInfo(profess, new SavedClassInformation(entry.getValue().getAsJsonObject()));
+					} catch (IllegalArgumentException exception) {
+						MMOCore.log(Level.WARNING, "Could not load class info '" + entry.getKey() + "': " + exception.getMessage());
+					}
+				}	
+			}
+		} catch (SQLException e) {
+			MMOCore.log(Level.SEVERE, "Failed to load playerdata from MySQL!");
+			e.printStackTrace();
+		}
+	}
+
+	private boolean isEmpty(String s) {
+		return s.equalsIgnoreCase("null") || s.equalsIgnoreCase("{}")
+			|| s.equalsIgnoreCase("[]") || s.equalsIgnoreCase("");
+	}
+
+	@Override
+	public void saveData(PlayerData data) {
+		MMOSQL sql = new MMOSQL(Table.PLAYERDATA, data.getUniqueId());
+		
+		sql.updateData("class_points", data.getClassPoints());
+		sql.updateData("skill_points", data.getSkillPoints());
+		sql.updateData("attribute_points", data.getAttributePoints());
+		sql.updateData("attribute_realloc_points", data.getAttributeReallocationPoints());
+		sql.updateData("level", data.getLevel());
+		sql.updateData("experience", data.getExperience());
+		sql.updateData("class", data.getProfess().getId());
+		sql.updateData("last_login", data.getLastLogin());
+		sql.updateData("guild", data.hasGuild() ? data.getGuild().getId() : null);
+		
+		sql.updateJSONArray("waypoints", data.getWaypoints());
+		sql.updateJSONArray("friends", data.getFriends().stream().map(uuid -> uuid.toString()).collect(Collectors.toList()));
+		sql.updateJSONArray("bound_skills", data.getBoundSkills().stream().map(skill -> skill.getSkill().getId()).collect(Collectors.toList()));
+
+		sql.updateJSONObject("skills", data.mapSkillLevels().entrySet());
+
+		sql.updateData("attributes", data.getAttributes().toJsonString());
+		sql.updateData("professions", data.getCollectionSkills().toJsonString());
+		sql.updateData("quests", data.getQuestData().toJsonString());
+
+		sql.updateData("class_info", createClassInfoData(data).toString());
+	}
+	
+	private JsonObject createClassInfoData(PlayerData data) {
+		JsonObject json = new JsonObject();
+		for(String c : data.getSavedClasses()) {
+			SavedClassInformation info = data.getClassInfo(c);
+			JsonObject classinfo = new JsonObject();
+			classinfo.addProperty("level", info.getLevel());
+			classinfo.addProperty("experience", info.getExperience());
+			classinfo.addProperty("skill-points", info.getSkillPoints());
+			classinfo.addProperty("attribute-points", info.getAttributePoints());
+			classinfo.addProperty("attribute-realloc-points", info.getAttributeReallocationPoints());
+			JsonObject skillinfo = new JsonObject();
+			for(String skill : info.getSkillKeys())
+				skillinfo.addProperty(skill, info.getSkillLevel(skill));
+			classinfo.add("skill", skillinfo);
+			JsonObject attributeinfo = new JsonObject();
+			for(String attribute : info.getAttributeKeys())
+				attributeinfo.addProperty(attribute, info.getAttributeLevel(attribute));
+			classinfo.add("attribute", attributeinfo);
+			
+			json.add(c, classinfo);
+		}
+		
+		return json;
+	}
+
+	@Override
+	public OfflinePlayerData getOffline(UUID uuid) {
+		return isLoaded(uuid) ? get(uuid) : new MySQLOfflinePlayerData(uuid);
+	}
+}
diff --git a/src/main/java/net/Indyuce/mmocore/manager/data/PlayerDataManager.java b/src/main/java/net/Indyuce/mmocore/manager/data/PlayerDataManager.java
index fd290888..4f541fe8 100644
--- a/src/main/java/net/Indyuce/mmocore/manager/data/PlayerDataManager.java
+++ b/src/main/java/net/Indyuce/mmocore/manager/data/PlayerDataManager.java
@@ -10,7 +10,6 @@ import org.bukkit.entity.Player;
 
 import net.Indyuce.mmocore.api.player.OfflinePlayerData;
 import net.Indyuce.mmocore.api.player.PlayerData;
-import net.Indyuce.mmocore.api.player.SimpleOfflinePlayerData;
 
 public abstract class PlayerDataManager {
 	private final Map<UUID, PlayerData> map = new HashMap<>();
@@ -23,9 +22,7 @@ public abstract class PlayerDataManager {
 		return map.get(uuid);
 	}
 
-	public OfflinePlayerData getOffline(UUID uuid) {
-		return isLoaded(uuid) ? get(uuid) : new SimpleOfflinePlayerData(uuid);
-	}
+	public abstract OfflinePlayerData getOffline(UUID uuid);
 
 	public PlayerData setup(Player player) {
 
diff --git a/src/main/java/net/Indyuce/mmocore/manager/data/YAMLGuildDataManager.java b/src/main/java/net/Indyuce/mmocore/manager/data/YAMLGuildDataManager.java
new file mode 100644
index 00000000..d7c83f95
--- /dev/null
+++ b/src/main/java/net/Indyuce/mmocore/manager/data/YAMLGuildDataManager.java
@@ -0,0 +1,48 @@
+package net.Indyuce.mmocore.manager.data;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import org.bukkit.configuration.file.FileConfiguration;
+import org.bukkit.configuration.file.YamlConfiguration;
+
+import net.Indyuce.mmocore.MMOCore;
+import net.Indyuce.mmocore.api.ConfigFile;
+import net.Indyuce.mmocore.api.player.social.guilds.Guild;
+
+public class YAMLGuildDataManager extends GuildDataManager {
+	@Override
+	public void save(Guild guild) {
+		ConfigFile config = new ConfigFile(guild);
+		config.getConfig().set("name", guild.getName());
+		config.getConfig().set("tag", guild.getTag());
+		config.getConfig().set("owner", guild.getOwner().toString());
+		
+		List<String> memberList = new ArrayList<>();
+		guild.getMembers().forEach(uuid -> memberList.add(uuid.toString()));
+		config.getConfig().set("members", memberList);
+		
+		config.save();
+	}
+	
+	@Override
+	public void load() {
+		File guildsFolder = new File(MMOCore.plugin.getDataFolder(), "guilds");
+		if(!guildsFolder.exists()) guildsFolder.mkdirs();
+	    for (File file : guildsFolder.listFiles()) {
+	        if (!file.isDirectory() && file.getName().substring(file.getName().lastIndexOf('.')).equalsIgnoreCase(".yml")) {
+	        	FileConfiguration c = YamlConfiguration.loadConfiguration(file);
+	        	Guild guild = newRegisteredGuild(UUID.fromString(c.getString("owner")), c.getString("name"), c.getString("tag"));
+	        	for(String m : c.getStringList("members"))
+	        		guild.registerMember(UUID.fromString(m));
+	        }
+	    }
+	}
+
+	@Override
+	public void delete(Guild guild) {
+		new ConfigFile(guild).delete();
+	}
+}
diff --git a/src/main/java/net/Indyuce/mmocore/api/player/SimpleOfflinePlayerData.java b/src/main/java/net/Indyuce/mmocore/manager/data/YAMLOfflinePlayerData.java
similarity index 82%
rename from src/main/java/net/Indyuce/mmocore/api/player/SimpleOfflinePlayerData.java
rename to src/main/java/net/Indyuce/mmocore/manager/data/YAMLOfflinePlayerData.java
index 302455fc..bf56f99f 100644
--- a/src/main/java/net/Indyuce/mmocore/api/player/SimpleOfflinePlayerData.java
+++ b/src/main/java/net/Indyuce/mmocore/manager/data/YAMLOfflinePlayerData.java
@@ -1,20 +1,21 @@
-package net.Indyuce.mmocore.api.player;
+package net.Indyuce.mmocore.manager.data;
 
 import java.util.List;
 import java.util.UUID;
 
 import net.Indyuce.mmocore.MMOCore;
 import net.Indyuce.mmocore.api.ConfigFile;
+import net.Indyuce.mmocore.api.player.OfflinePlayerData;
 import net.Indyuce.mmocore.api.player.profess.PlayerClass;
 
-public class SimpleOfflinePlayerData extends OfflinePlayerData {
+public class YAMLOfflinePlayerData extends OfflinePlayerData {
 	private final ConfigFile config;
 
 	/*
 	 * supports offline player data operations like friend removals which can't
 	 * be handled when their player data is not loaded in the data map.
 	 */
-	public SimpleOfflinePlayerData(UUID uuid) {
+	public YAMLOfflinePlayerData(UUID uuid) {
 		super(uuid);
 
 		config = new ConfigFile(uuid);
diff --git a/src/main/java/net/Indyuce/mmocore/manager/data/YAMLPlayerDataManager.java b/src/main/java/net/Indyuce/mmocore/manager/data/YAMLPlayerDataManager.java
index 2aea9eec..ad9df775 100644
--- a/src/main/java/net/Indyuce/mmocore/manager/data/YAMLPlayerDataManager.java
+++ b/src/main/java/net/Indyuce/mmocore/manager/data/YAMLPlayerDataManager.java
@@ -11,6 +11,7 @@ import org.bukkit.configuration.file.FileConfiguration;
 
 import net.Indyuce.mmocore.MMOCore;
 import net.Indyuce.mmocore.api.ConfigFile;
+import net.Indyuce.mmocore.api.player.OfflinePlayerData;
 import net.Indyuce.mmocore.api.player.PlayerData;
 import net.Indyuce.mmocore.api.player.profess.PlayerClass;
 import net.Indyuce.mmocore.api.player.profess.SavedClassInformation;
@@ -34,7 +35,7 @@ public class YAMLPlayerDataManager extends PlayerDataManager {
 		data.setStamina(data.getStats().getStat(StatType.MAX_STAMINA));
 		data.setStellium(data.getStats().getStat(StatType.MAX_STELLIUM));
 		if (config.contains("guild"))
-			data.setGuild(MMOCore.plugin.guildManager.stillInGuild(data.getUniqueId(), config.getString("guild")));
+			data.setGuild(MMOCore.plugin.dataProvider.getGuildManager().stillInGuild(data.getUniqueId(), config.getString("guild")));
 		if (config.contains("attribute"))
 			data.getAttributes().load(config.getConfigurationSection("attribute"));
 		if (config.contains("profession"))
@@ -119,4 +120,9 @@ public class YAMLPlayerDataManager extends PlayerDataManager {
 
 		file.save();
 	}
+
+	@Override
+	public OfflinePlayerData getOffline(UUID uuid) {
+		return isLoaded(uuid) ? get(uuid) : new YAMLOfflinePlayerData(uuid);
+	}
 }
diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml
index 0fbd305e..70dcdaff 100644
--- a/src/main/resources/config.yml
+++ b/src/main/resources/config.yml
@@ -7,6 +7,16 @@ auto-save:
     # In seconds (1800 = 30 minutes)
     interval: 1800
 
+# MySQL Support
+mysql:
+  enabled: false
+  database: minecraft
+  host: localhost
+  port: 3306
+  user: mmolover
+  pass: ILoveAria
+  flags: "?allowReconnect=true"
+
 # The list of all conditions which must be met for the
 # BLOCK REGEN and BLOCK RESTRICTIONS to apply.
 # Set to 'custom-mine-conditions: []' for no condition.