diff --git a/bukkit/src/main/resources/config.yml b/bukkit/src/main/resources/config.yml index ede47790b..655b3255a 100644 --- a/bukkit/src/main/resources/config.yml +++ b/bukkit/src/main/resources/config.yml @@ -47,6 +47,18 @@ log-notify: true # Fill out connection info below if you're using MySQL or MongoDB storage-method: h2 +# This block enables support for split datastores. +# Only touch this if you're sure that you know what you're doing. +# I (the author) do not endorse nor recommend the use of this feature. +split-storage: + enabled: false + methods: + user: h2 + group: h2 + track: h2 + uuid: h2 + log: h2 + data: address: localhost:3306 database: minecraft diff --git a/bungee/src/main/resources/config.yml b/bungee/src/main/resources/config.yml index db7d394ae..fa69ea7b0 100644 --- a/bungee/src/main/resources/config.yml +++ b/bungee/src/main/resources/config.yml @@ -47,6 +47,18 @@ log-notify: true # Fill out connection info below if you're using MySQL or MongoDB storage-method: h2 +# This block enables support for split datastores. +# Only touch this if you're sure that you know what you're doing. +# I (the author) do not endorse nor recommend the use of this feature. +split-storage: + enabled: false + methods: + user: h2 + group: h2 + track: h2 + uuid: h2 + log: h2 + data: address: localhost:3306 database: minecraft diff --git a/common/src/main/java/me/lucko/luckperms/core/LPConfiguration.java b/common/src/main/java/me/lucko/luckperms/core/LPConfiguration.java index 526eb7b72..747757bc6 100644 --- a/common/src/main/java/me/lucko/luckperms/core/LPConfiguration.java +++ b/common/src/main/java/me/lucko/luckperms/core/LPConfiguration.java @@ -28,6 +28,9 @@ import me.lucko.luckperms.LuckPermsPlugin; import me.lucko.luckperms.constants.Patterns; import me.lucko.luckperms.storage.DatastoreConfiguration; +import java.util.HashMap; +import java.util.Map; + public abstract class LPConfiguration { @Getter(AccessLevel.PROTECTED) @@ -118,4 +121,19 @@ public abstract class LPConfiguration { public String getStorageMethod() { return getString("storage-method", defaultStorage); } + + public boolean getSplitStorage() { + return getBoolean("split-storage.enabled", false); + } + + public Map getSplitStorageOptions() { + Map map = new HashMap<>(); + map.put("user", getString("split-storage.methods.user", defaultStorage)); + map.put("group", getString("split-storage.methods.group", defaultStorage)); + map.put("track", getString("split-storage.methods.track", defaultStorage)); + map.put("uuid", getString("split-storage.methods.uuid", defaultStorage)); + map.put("log", getString("split-storage.methods.log", defaultStorage)); + + return map; + } } diff --git a/common/src/main/java/me/lucko/luckperms/storage/SplitDatastore.java b/common/src/main/java/me/lucko/luckperms/storage/SplitDatastore.java new file mode 100644 index 000000000..acecf3965 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/storage/SplitDatastore.java @@ -0,0 +1,136 @@ +package me.lucko.luckperms.storage; + +import com.google.common.collect.ImmutableMap; +import me.lucko.luckperms.LuckPermsPlugin; +import me.lucko.luckperms.api.LogEntry; +import me.lucko.luckperms.data.Log; +import me.lucko.luckperms.groups.Group; +import me.lucko.luckperms.tracks.Track; +import me.lucko.luckperms.users.User; + +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +public class SplitDatastore extends Datastore { + private final Map backing; + private final Map types; + + protected SplitDatastore(LuckPermsPlugin plugin, Map backing, Map types) { + super(plugin, "Split Storage"); + this.backing = ImmutableMap.copyOf(backing); + this.types = ImmutableMap.copyOf(types); + } + + @Override + public void init() { + backing.values().forEach(Datastore::init); + for (Datastore ds : backing.values()) { + if (!ds.isAcceptingLogins()) { + return; + } + } + + setAcceptingLogins(true); + } + + @Override + public void shutdown() { + backing.values().forEach(Datastore::shutdown); + } + + @Override + public boolean logAction(LogEntry entry) { + return backing.get(types.get("log")).logAction(entry); + } + + @Override + public Log getLog() { + return backing.get(types.get("log")).getLog(); + } + + @Override + public boolean loadUser(UUID uuid, String username) { + return backing.get(types.get("user")).loadUser(uuid, username); + } + + @Override + public boolean saveUser(User user) { + return backing.get(types.get("user")).saveUser(user); + } + + @Override + public boolean cleanupUsers() { + return backing.get(types.get("user")).cleanupUsers(); + } + + @Override + public Set getUniqueUsers() { + return backing.get(types.get("user")).getUniqueUsers(); + } + + @Override + public boolean createAndLoadGroup(String name) { + return backing.get(types.get("group")).createAndLoadGroup(name); + } + + @Override + public boolean loadGroup(String name) { + return backing.get(types.get("group")).loadGroup(name); + } + + @Override + public boolean loadAllGroups() { + return backing.get(types.get("group")).loadAllGroups(); + } + + @Override + public boolean saveGroup(Group group) { + return backing.get(types.get("group")).saveGroup(group); + } + + @Override + public boolean deleteGroup(Group group) { + return backing.get(types.get("group")).deleteGroup(group); + } + + @Override + public boolean createAndLoadTrack(String name) { + return backing.get(types.get("track")).createAndLoadTrack(name); + } + + @Override + public boolean loadTrack(String name) { + return backing.get(types.get("track")).loadTrack(name); + } + + @Override + public boolean loadAllTracks() { + return backing.get(types.get("track")).loadAllTracks(); + } + + @Override + public boolean saveTrack(Track track) { + return backing.get(types.get("track")).saveTrack(track); + } + + @Override + public boolean deleteTrack(Track track) { + return backing.get(types.get("track")).deleteTrack(track); + } + + @Override + public boolean saveUUIDData(String username, UUID uuid) { + return backing.get(types.get("uuid")).saveUUIDData(username, uuid); + } + + @Override + public UUID getUUID(String username) { + return backing.get(types.get("uuid")).getUUID(username); + } + + @Override + public String getName(UUID uuid) { + return backing.get(types.get("uuid")).getName(uuid); + } +} diff --git a/common/src/main/java/me/lucko/luckperms/storage/StorageFactory.java b/common/src/main/java/me/lucko/luckperms/storage/StorageFactory.java index 68730f5d2..851c31c85 100644 --- a/common/src/main/java/me/lucko/luckperms/storage/StorageFactory.java +++ b/common/src/main/java/me/lucko/luckperms/storage/StorageFactory.java @@ -28,48 +28,73 @@ import me.lucko.luckperms.LuckPermsPlugin; import me.lucko.luckperms.storage.methods.*; import java.io.File; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; import java.util.Set; @UtilityClass public class StorageFactory { private static final Set TYPES = ImmutableSet.of("flatfile", "mongodb", "mysql", "sqlite", "h2"); + @SuppressWarnings("unchecked") public static Datastore getDatastore(LuckPermsPlugin plugin, String defaultMethod) { Datastore datastore; plugin.getLog().info("Detecting storage method..."); - String storageMethod = plugin.getConfiguration().getStorageMethod().toLowerCase(); + if (plugin.getConfiguration().getSplitStorage()) { + plugin.getLog().info("Using split storage."); - if (!TYPES.contains(storageMethod)) { - plugin.getLog().severe("Storage method '" + storageMethod + "' not recognised. Using the default instead."); - storageMethod = defaultMethod; - } + // java sucks + Map types = (Map) plugin.getConfiguration().getSplitStorageOptions(); - switch (storageMethod) { - case "mysql": - plugin.getLog().info("Using MySQL as storage method."); - datastore = new MySQLDatastore(plugin, plugin.getConfiguration().getDatabaseValues()); - break; - case "sqlite": - plugin.getLog().info("Using SQLite as storage method."); - datastore = new SQLiteDatastore(plugin, new File(plugin.getDataFolder(), "luckperms.sqlite")); - break; - case "h2": - plugin.getLog().info("Using H2 as storage method."); - datastore = new H2Datastore(plugin, new File(plugin.getDataFolder(), "luckperms.db")); - break; - case "mongodb": - plugin.getLog().info("Using MongoDB as storage method."); - datastore = new MongoDBDatastore(plugin, plugin.getConfiguration().getDatabaseValues()); - break; - default: - plugin.getLog().info("Using Flatfile (JSON) as storage method."); - datastore = new FlatfileDatastore(plugin, plugin.getDataFolder()); - break; + types.entrySet().stream() + .filter(e -> !TYPES.contains(e.getValue().toLowerCase())) + .forEach(e -> { + plugin.getLog().severe("Storage method for " + e.getKey() + " - " + e.getValue() + " not recognised. " + + "Using the default instead."); + e.setValue(defaultMethod); + }); + + Set neededTypes = new HashSet<>(); + neededTypes.addAll(types.values()); + + Map backing = new HashMap<>(); + + for (String type : neededTypes) { + backing.put(type, fromString(type, plugin)); + } + + datastore = new SplitDatastore(plugin, backing, types); + + } else { + String storageMethod = plugin.getConfiguration().getStorageMethod().toLowerCase(); + if (!TYPES.contains(storageMethod)) { + plugin.getLog().severe("Storage method '" + storageMethod + "' not recognised. Using the default instead."); + storageMethod = defaultMethod; + } + + datastore = fromString(storageMethod, plugin); + plugin.getLog().info("Using " + datastore.getName() + " as storage method."); } plugin.getLog().info("Initialising datastore..."); datastore.init(); return datastore; } + + private static Datastore fromString(String storageMethod, LuckPermsPlugin plugin) { + switch (storageMethod) { + case "mysql": + return new MySQLDatastore(plugin, plugin.getConfiguration().getDatabaseValues()); + case "sqlite": + return new SQLiteDatastore(plugin, new File(plugin.getDataFolder(), "luckperms.sqlite")); + case "h2": + return new H2Datastore(plugin, new File(plugin.getDataFolder(), "luckperms.db")); + case "mongodb": + return new MongoDBDatastore(plugin, plugin.getConfiguration().getDatabaseValues()); + default: + return new FlatfileDatastore(plugin, plugin.getDataFolder()); + } + } } diff --git a/common/src/main/java/me/lucko/luckperms/storage/methods/SQLiteDatastore.java b/common/src/main/java/me/lucko/luckperms/storage/methods/SQLiteDatastore.java index 964d12afc..b6a7b4fd3 100644 --- a/common/src/main/java/me/lucko/luckperms/storage/methods/SQLiteDatastore.java +++ b/common/src/main/java/me/lucko/luckperms/storage/methods/SQLiteDatastore.java @@ -66,7 +66,7 @@ public class SQLiteDatastore extends SQLDatastore { @Cleanup PreparedStatement preparedStatement = connection.prepareStatement(queryPS.getQuery()); queryPS.onRun(preparedStatement); preparedStatement.execute(); - + success = true; } catch (SQLException e) { e.printStackTrace(); diff --git a/sponge/src/main/resources/luckperms.conf b/sponge/src/main/resources/luckperms.conf index 80a480c2a..6190cc9fc 100644 --- a/sponge/src/main/resources/luckperms.conf +++ b/sponge/src/main/resources/luckperms.conf @@ -47,6 +47,20 @@ log-notify=true # Fill out connection info below if you're using MySQL or MongoDB storage-method="h2" +# This block enables support for split datastores. +# Only touch this if you're sure that you know what you're doing. +# I (the author) do not endorse nor recommend the use of this feature. +split-storage: { + enabled=false + methods: { + user="h2" + group="h2" + track="h2" + uuid="h2" + log="h2" + } +} + data: { address="localhost:3306" database="minecraft"