From a15a0752f4dad6881a8875bfb03b9f8f46333c85 Mon Sep 17 00:00:00 2001 From: Luck Date: Fri, 19 Aug 2016 18:21:16 +0100 Subject: [PATCH] Add support for MongoDB and H2 database formats --- README.md | 2 +- api/pom.xml | 2 +- .../lucko/luckperms/api/LPConfiguration.java | 9 + .../api/data/DatastoreConfiguration.java | 33 ++ .../api/data/MySQLConfiguration.java | 4 + bukkit/pom.xml | 35 +- .../me/lucko/luckperms/LPBukkitPlugin.java | 28 +- bukkit/src/main/resources/config.yml | 18 +- bungee/pom.xml | 30 +- .../me/lucko/luckperms/LPBungeePlugin.java | 24 +- bungee/src/main/resources/config.yml | 18 +- common/pom.xml | 23 +- .../me/lucko/luckperms/LuckPermsPlugin.java | 11 + .../internal/LPConfigurationLink.java | 7 + .../lucko/luckperms/core/LPConfiguration.java | 16 +- .../me/lucko/luckperms/data/LogEntry.java | 9 +- ...ation.java => DatastoreConfiguration.java} | 2 +- .../luckperms/storage/StorageFactory.java | 75 +++ .../storage/methods/H2Datastore.java | 120 +++++ .../storage/methods/MongoDBDatastore.java | 453 ++++++++++++++++++ .../storage/methods/MySQLDatastore.java | 6 +- .../storage/methods/SQLDatastore.java | 10 +- pom.xml | 2 +- sponge/pom.xml | 26 +- .../me/lucko/luckperms/LPSpongePlugin.java | 33 +- sponge/src/main/resources/luckperms.conf | 18 +- 26 files changed, 877 insertions(+), 137 deletions(-) create mode 100644 api/src/main/java/me/lucko/luckperms/api/data/DatastoreConfiguration.java rename common/src/main/java/me/lucko/luckperms/storage/{MySQLConfiguration.java => DatastoreConfiguration.java} (93%) create mode 100644 common/src/main/java/me/lucko/luckperms/storage/StorageFactory.java create mode 100644 common/src/main/java/me/lucko/luckperms/storage/methods/H2Datastore.java create mode 100644 common/src/main/java/me/lucko/luckperms/storage/methods/MongoDBDatastore.java diff --git a/README.md b/README.md index e80dd0916..3a09d94ee 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ A permissions implementation for Bukkit/Spigot, BungeeCord and Sponge. * **Open Sourced, Free...** - you shouldn't have to pay $10+ for a "powerful" permissions plugin. * **BungeeCord compatible** - permissions, users and groups are synced across all LuckPerms instances * **Sponge compatible** - permissions, users and groups are synced across all LuckPerms instances (bukkit --> sponge, for example) -* **Support for MySQL, SQLite & Flatfile (JSON)** - other storage methods coming soon (maybe) +* **Support for MySQL, MongoDB, SQLite, H2 & Flatfile (JSON)** - other storage methods coming soon (maybe) ## Setup All configuration options are in the **config.yml/luckperms.conf** file, which is generated automagically when the plugin first starts. diff --git a/api/pom.xml b/api/pom.xml index 5e0803e8d..120668505 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -5,7 +5,7 @@ luckperms me.lucko.luckperms - 2.3 + 2.4 4.0.0 diff --git a/api/src/main/java/me/lucko/luckperms/api/LPConfiguration.java b/api/src/main/java/me/lucko/luckperms/api/LPConfiguration.java index 6de25caab..195f9a75a 100644 --- a/api/src/main/java/me/lucko/luckperms/api/LPConfiguration.java +++ b/api/src/main/java/me/lucko/luckperms/api/LPConfiguration.java @@ -22,6 +22,7 @@ package me.lucko.luckperms.api; +import me.lucko.luckperms.api.data.DatastoreConfiguration; import me.lucko.luckperms.api.data.MySQLConfiguration; /** @@ -76,9 +77,17 @@ public interface LPConfiguration { /** * @return the database values set in the configuration + * @deprecated use {@link #getDatastoreConfig()} */ + @SuppressWarnings("deprecation") + @Deprecated MySQLConfiguration getDatabaseValues(); + /** + * @return the values set for data storage in the configuration + */ + DatastoreConfiguration getDatastoreConfig(); + /** * @return the storage method string from the configuration */ diff --git a/api/src/main/java/me/lucko/luckperms/api/data/DatastoreConfiguration.java b/api/src/main/java/me/lucko/luckperms/api/data/DatastoreConfiguration.java new file mode 100644 index 000000000..4db0a1882 --- /dev/null +++ b/api/src/main/java/me/lucko/luckperms/api/data/DatastoreConfiguration.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2016 Lucko (Luck) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.api.data; + +@SuppressWarnings("deprecation") +public interface DatastoreConfiguration extends MySQLConfiguration { + + String getAddress(); + String getDatabase(); + String getUsername(); + String getPassword(); + +} diff --git a/api/src/main/java/me/lucko/luckperms/api/data/MySQLConfiguration.java b/api/src/main/java/me/lucko/luckperms/api/data/MySQLConfiguration.java index 800c6ec48..b564fecca 100644 --- a/api/src/main/java/me/lucko/luckperms/api/data/MySQLConfiguration.java +++ b/api/src/main/java/me/lucko/luckperms/api/data/MySQLConfiguration.java @@ -22,6 +22,10 @@ package me.lucko.luckperms.api.data; +/** + * @deprecated Use {@link DatastoreConfiguration}. This is now used by multiple datastores, not just MySQL. + */ +@Deprecated public interface MySQLConfiguration { String getAddress(); diff --git a/bukkit/pom.xml b/bukkit/pom.xml index 531bd8c51..992020079 100644 --- a/bukkit/pom.xml +++ b/bukkit/pom.xml @@ -5,7 +5,7 @@ luckperms me.lucko.luckperms - 2.3 + 2.4 4.0.0 @@ -43,7 +43,12 @@ shade - true + false + + + org.xerial:* + + org.slf4j @@ -53,6 +58,18 @@ com.zaxxer.hikari me.lucko.luckperms.lib.hikari + + com.mongodb + me.lucko.luckperms.lib.mongodb + + + org.bson + me.lucko.luckperms.lib.bson + + + org.h2 + me.lucko.luckperms.lib.h2 + @@ -105,20 +122,6 @@ ${project.version} compile - - - com.zaxxer - HikariCP - 2.4.7 - compile - - - - org.slf4j - slf4j-simple - 1.7.9 - compile - org.projectlombok diff --git a/bukkit/src/main/java/me/lucko/luckperms/LPBukkitPlugin.java b/bukkit/src/main/java/me/lucko/luckperms/LPBukkitPlugin.java index 3307b0bcd..b31710515 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/LPBukkitPlugin.java +++ b/bukkit/src/main/java/me/lucko/luckperms/LPBukkitPlugin.java @@ -35,9 +35,7 @@ import me.lucko.luckperms.data.Importer; import me.lucko.luckperms.groups.GroupManager; import me.lucko.luckperms.runnables.UpdateTask; import me.lucko.luckperms.storage.Datastore; -import me.lucko.luckperms.storage.methods.FlatfileDatastore; -import me.lucko.luckperms.storage.methods.MySQLDatastore; -import me.lucko.luckperms.storage.methods.SQLiteDatastore; +import me.lucko.luckperms.storage.StorageFactory; import me.lucko.luckperms.tracks.TrackManager; import me.lucko.luckperms.users.BukkitUserManager; import me.lucko.luckperms.users.UserManager; @@ -83,24 +81,7 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin { main.setTabCompleter(commandManager); main.setAliases(Arrays.asList("perms", "lp", "permissions", "p", "perm")); - getLog().info("Detecting storage method..."); - final String storageMethod = configuration.getStorageMethod(); - if (storageMethod.equalsIgnoreCase("mysql")) { - getLog().info("Using MySQL as storage method."); - datastore = new MySQLDatastore(this, configuration.getDatabaseValues()); - } else if (storageMethod.equalsIgnoreCase("sqlite")) { - getLog().info("Using SQLite as storage method."); - datastore = new SQLiteDatastore(this, new File(getDataFolder(), "luckperms.sqlite")); - } else if (storageMethod.equalsIgnoreCase("flatfile")) { - getLog().info("Using Flatfile (JSON) as storage method."); - datastore = new FlatfileDatastore(this, getDataFolder()); - } else { - getLog().severe("Storage method '" + storageMethod + "' was not recognised. Using SQLite as fallback."); - datastore = new SQLiteDatastore(this, new File(getDataFolder(), "luckperms.sqlite")); - } - - getLog().info("Initialising datastore..."); - datastore.init(); + datastore = StorageFactory.getDatastore(this, "sqlite"); getLog().info("Loading internal permission managers..."); uuidCache = new UuidCache(getConfiguration().getOnlineMode()); @@ -197,6 +178,11 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin { return getServer().getOnlinePlayers().stream().map(p -> BukkitSenderFactory.get().wrap(p)).collect(Collectors.toList()); } + @Override + public Sender getConsoleSender() { + return BukkitSenderFactory.get().wrap(getServer().getConsoleSender()); + } + @Override public List getPossiblePermissions() { final List perms = new ArrayList<>(); diff --git a/bukkit/src/main/resources/config.yml b/bukkit/src/main/resources/config.yml index 5a134e9d7..5b3d720c1 100644 --- a/bukkit/src/main/resources/config.yml +++ b/bukkit/src/main/resources/config.yml @@ -1,4 +1,9 @@ -# LuckPerms Configuration +############################################################################## +# +------------------------------------------------------------------------+ # +# | LuckPerms Configuration | # +# | https://github.com/lucko/LuckPerms | # +# +------------------------------------------------------------------------+ # +############################################################################## # The name of the server, used for server specific permissions. Set to 'global' to disable. server: global @@ -38,13 +43,16 @@ apply-regex: true apply-shorthand: true # Which storage method the plugin should use. -# Currently supported: mysql, sqlite, flatfile -# Fill out connection info below if you're using MySQL -storage-method: sqlite +# Currently supported: mysql, sqlite, h2, flatfile, mongodb +# Fill out connection info below if you're using MySQL or MongoDB +storage-method: h2 -sql: +data: address: localhost:3306 database: minecraft username: root password: '' + + # Set to -1 to disable. If this is the only instance accessing the datastore, you can disable syncing. + # e.g. if you're using sqlite or flatfile, this can be set to -1 to save resources. sync-minutes: 3 \ No newline at end of file diff --git a/bungee/pom.xml b/bungee/pom.xml index f53006484..e65ce170b 100644 --- a/bungee/pom.xml +++ b/bungee/pom.xml @@ -5,7 +5,7 @@ luckperms me.lucko.luckperms - 2.3 + 2.4 4.0.0 @@ -43,7 +43,7 @@ shade - true + false org.slf4j @@ -53,6 +53,18 @@ com.zaxxer.hikari me.lucko.luckperms.lib.hikari + + com.mongodb + me.lucko.luckperms.lib.mongodb + + + org.bson + me.lucko.luckperms.lib.bson + + + org.h2 + me.lucko.luckperms.lib.h2 + @@ -98,20 +110,6 @@ ${project.version} compile - - - com.zaxxer - HikariCP - 2.4.7 - compile - - - - org.slf4j - slf4j-simple - 1.7.9 - compile - org.projectlombok diff --git a/bungee/src/main/java/me/lucko/luckperms/LPBungeePlugin.java b/bungee/src/main/java/me/lucko/luckperms/LPBungeePlugin.java index bc87db16a..3671b615c 100644 --- a/bungee/src/main/java/me/lucko/luckperms/LPBungeePlugin.java +++ b/bungee/src/main/java/me/lucko/luckperms/LPBungeePlugin.java @@ -34,8 +34,7 @@ import me.lucko.luckperms.data.Importer; import me.lucko.luckperms.groups.GroupManager; import me.lucko.luckperms.runnables.UpdateTask; import me.lucko.luckperms.storage.Datastore; -import me.lucko.luckperms.storage.methods.FlatfileDatastore; -import me.lucko.luckperms.storage.methods.MySQLDatastore; +import me.lucko.luckperms.storage.StorageFactory; import me.lucko.luckperms.tracks.TrackManager; import me.lucko.luckperms.users.BungeeUserManager; import me.lucko.luckperms.users.UserManager; @@ -78,21 +77,7 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin { // disable the default Bungee /perms command so it gets handled by the Bukkit plugin getProxy().getDisabledCommands().add("perms"); - getLog().info("Detecting storage method..."); - final String storageMethod = configuration.getStorageMethod(); - if (storageMethod.equalsIgnoreCase("mysql")) { - getLog().info("Using MySQL as storage method."); - datastore = new MySQLDatastore(this, configuration.getDatabaseValues()); - } else if (storageMethod.equalsIgnoreCase("flatfile")) { - getLog().info("Using Flatfile (JSON) as storage method."); - datastore = new FlatfileDatastore(this, getDataFolder()); - } else { - getLog().severe("Storage method '" + storageMethod + "' was not recognised. Using Flatfile as fallback."); - datastore = new FlatfileDatastore(this, getDataFolder()); - } - - getLog().info("Initialising datastore..."); - datastore.init(); + datastore = StorageFactory.getDatastore(this, "h2"); getLog().info("Loading internal permission managers..."); uuidCache = new UuidCache(getConfiguration().getOnlineMode()); @@ -162,6 +147,11 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin { return getProxy().getPlayers().stream().map(p -> BungeeSenderFactory.get().wrap(p)).collect(Collectors.toList()); } + @Override + public Sender getConsoleSender() { + return BungeeSenderFactory.get().wrap(getProxy().getConsole()); + } + @Override public List getPossiblePermissions() { // No such thing on Bungee. Wildcards are processed in the listener instead. diff --git a/bungee/src/main/resources/config.yml b/bungee/src/main/resources/config.yml index 558fdd023..2f1f846ae 100644 --- a/bungee/src/main/resources/config.yml +++ b/bungee/src/main/resources/config.yml @@ -1,4 +1,9 @@ -# LuckPerms Configuration +############################################################################## +# +------------------------------------------------------------------------+ # +# | LuckPerms Configuration | # +# | https://github.com/lucko/LuckPerms | # +# +------------------------------------------------------------------------+ # +############################################################################## # The name of the server, used for server specific permissions. Set to 'global' to disable. server: bungee @@ -38,13 +43,16 @@ apply-regex: true apply-shorthand: true # Which storage method the plugin should use. -# Currently supported: mysql & flatfile -# Fill out connection info below if you're using MySQL -storage-method: flatfile +# Currently supported: mysql, sqlite, h2, flatfile, mongodb +# Fill out connection info below if you're using MySQL or MongoDB +storage-method: h2 -sql: +data: address: localhost:3306 database: minecraft username: root password: '' + + # Set to -1 to disable. If this is the only instance accessing the datastore, you can disable syncing. + # e.g. if you're using sqlite or flatfile, this can be set to -1 to save resources. sync-minutes: 3 \ No newline at end of file diff --git a/common/pom.xml b/common/pom.xml index b249f75af..72211ed95 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -5,7 +5,7 @@ luckperms me.lucko.luckperms - 2.3 + 2.4 4.0.0 @@ -39,6 +39,27 @@ 2.4.7 compile + + + org.xerial + sqlite-jdbc + 3.8.11.2 + compile + + + + com.h2database + h2 + 1.4.192 + compile + + + + org.mongodb + mongo-java-driver + 3.3.0 + compile + org.slf4j diff --git a/common/src/main/java/me/lucko/luckperms/LuckPermsPlugin.java b/common/src/main/java/me/lucko/luckperms/LuckPermsPlugin.java index 43e8d5a4d..ee98bafac 100644 --- a/common/src/main/java/me/lucko/luckperms/LuckPermsPlugin.java +++ b/common/src/main/java/me/lucko/luckperms/LuckPermsPlugin.java @@ -96,6 +96,11 @@ public interface LuckPermsPlugin { */ File getMainDir(); + /** + * @return the platforms data folder + */ + File getDataFolder(); + /** * @return the importer instance for the platform */ @@ -126,6 +131,12 @@ public interface LuckPermsPlugin { */ List getSenders(); + /** + * Gets the console sender of the instance + * @return a the console sender of the instance + */ + Sender getConsoleSender(); + /** * Gets all possible permission nodes, used for resolving wildcards * @return a {@link List} of permission nodes diff --git a/common/src/main/java/me/lucko/luckperms/api/implementation/internal/LPConfigurationLink.java b/common/src/main/java/me/lucko/luckperms/api/implementation/internal/LPConfigurationLink.java index 38f137d91..6d779b571 100644 --- a/common/src/main/java/me/lucko/luckperms/api/implementation/internal/LPConfigurationLink.java +++ b/common/src/main/java/me/lucko/luckperms/api/implementation/internal/LPConfigurationLink.java @@ -24,6 +24,7 @@ package me.lucko.luckperms.api.implementation.internal; import lombok.AllArgsConstructor; import me.lucko.luckperms.api.LPConfiguration; +import me.lucko.luckperms.api.data.DatastoreConfiguration; import me.lucko.luckperms.api.data.MySQLConfiguration; /** @@ -78,8 +79,14 @@ public class LPConfigurationLink implements LPConfiguration { return master.getApplyShorthand(); } + @SuppressWarnings("deprecation") @Override public MySQLConfiguration getDatabaseValues() { + return getDatastoreConfig(); + } + + @Override + public DatastoreConfiguration getDatastoreConfig() { return master.getDatabaseValues(); } 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 5ab4a5868..c1d2f40ec 100644 --- a/common/src/main/java/me/lucko/luckperms/core/LPConfiguration.java +++ b/common/src/main/java/me/lucko/luckperms/core/LPConfiguration.java @@ -26,7 +26,7 @@ import lombok.AccessLevel; import lombok.Getter; import me.lucko.luckperms.LuckPermsPlugin; import me.lucko.luckperms.constants.Patterns; -import me.lucko.luckperms.storage.MySQLConfiguration; +import me.lucko.luckperms.storage.DatastoreConfiguration; public abstract class LPConfiguration { @@ -70,7 +70,7 @@ public abstract class LPConfiguration { } public int getSyncTime() { - return getInt("sql.sync-minutes", 3); + return getInt("data.sync-minutes", 3); } public String getDefaultGroupNode() { @@ -101,12 +101,12 @@ public abstract class LPConfiguration { return getBoolean("apply-shorthand", true); } - public MySQLConfiguration getDatabaseValues() { - return new MySQLConfiguration( - getString("sql.address", null), - getString("sql.database", null), - getString("sql.username", null), - getString("sql.password", null) + public DatastoreConfiguration getDatabaseValues() { + return new DatastoreConfiguration( + getString("data.address", null), + getString("data.database", null), + getString("data.username", null), + getString("data.password", null) ); } diff --git a/common/src/main/java/me/lucko/luckperms/data/LogEntry.java b/common/src/main/java/me/lucko/luckperms/data/LogEntry.java index 7f1733dfa..3015d7601 100644 --- a/common/src/main/java/me/lucko/luckperms/data/LogEntry.java +++ b/common/src/main/java/me/lucko/luckperms/data/LogEntry.java @@ -31,6 +31,9 @@ import me.lucko.luckperms.groups.Group; import me.lucko.luckperms.tracks.Track; import me.lucko.luckperms.users.User; +import java.util.List; +import java.util.stream.Collectors; + public class LogEntry extends me.lucko.luckperms.api.LogEntry { public static LogEntryBuilder build() { return new LogEntryBuilder(); @@ -45,8 +48,12 @@ public class LogEntry extends me.lucko.luckperms.api.LogEntry { final String msg = super.getFormatted(); - plugin.getSenders().stream() + List senders = plugin.getSenders().stream() .filter(Permission.LOG_NOTIFY::isAuthorized) + .collect(Collectors.toList()); + senders.add(plugin.getConsoleSender()); + + senders.stream() .filter(s -> !plugin.getIgnoringLogs().contains(s.getUuid())) .forEach(s -> Message.LOG.send(s, msg)); } diff --git a/common/src/main/java/me/lucko/luckperms/storage/MySQLConfiguration.java b/common/src/main/java/me/lucko/luckperms/storage/DatastoreConfiguration.java similarity index 93% rename from common/src/main/java/me/lucko/luckperms/storage/MySQLConfiguration.java rename to common/src/main/java/me/lucko/luckperms/storage/DatastoreConfiguration.java index 09ed0a883..a492d537e 100644 --- a/common/src/main/java/me/lucko/luckperms/storage/MySQLConfiguration.java +++ b/common/src/main/java/me/lucko/luckperms/storage/DatastoreConfiguration.java @@ -27,7 +27,7 @@ import lombok.Getter; @Getter @AllArgsConstructor -public class MySQLConfiguration implements me.lucko.luckperms.api.data.MySQLConfiguration { +public class DatastoreConfiguration implements me.lucko.luckperms.api.data.DatastoreConfiguration { private final String address; private final String database; diff --git a/common/src/main/java/me/lucko/luckperms/storage/StorageFactory.java b/common/src/main/java/me/lucko/luckperms/storage/StorageFactory.java new file mode 100644 index 000000000..68730f5d2 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/storage/StorageFactory.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2016 Lucko (Luck) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.storage; + +import com.google.common.collect.ImmutableSet; +import lombok.experimental.UtilityClass; +import me.lucko.luckperms.LuckPermsPlugin; +import me.lucko.luckperms.storage.methods.*; + +import java.io.File; +import java.util.Set; + +@UtilityClass +public class StorageFactory { + private static final Set TYPES = ImmutableSet.of("flatfile", "mongodb", "mysql", "sqlite", "h2"); + + public static Datastore getDatastore(LuckPermsPlugin plugin, String defaultMethod) { + Datastore datastore; + + plugin.getLog().info("Detecting storage method..."); + String storageMethod = plugin.getConfiguration().getStorageMethod().toLowerCase(); + + if (!TYPES.contains(storageMethod)) { + plugin.getLog().severe("Storage method '" + storageMethod + "' not recognised. Using the default instead."); + storageMethod = defaultMethod; + } + + 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; + } + + plugin.getLog().info("Initialising datastore..."); + datastore.init(); + return datastore; + } +} diff --git a/common/src/main/java/me/lucko/luckperms/storage/methods/H2Datastore.java b/common/src/main/java/me/lucko/luckperms/storage/methods/H2Datastore.java new file mode 100644 index 000000000..247888dcf --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/storage/methods/H2Datastore.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2016 Lucko (Luck) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.storage.methods; + +import lombok.Cleanup; +import me.lucko.luckperms.LuckPermsPlugin; + +import java.io.File; +import java.sql.*; + +public class H2Datastore extends SQLDatastore { + + private static final String CREATETABLE_UUID = "CREATE TABLE IF NOT EXISTS `lp_uuid` (`name` VARCHAR(16) NOT NULL, `uuid` VARCHAR(36) NOT NULL, PRIMARY KEY (`name`)) DEFAULT CHARSET=utf8;"; + private static final String CREATETABLE_USERS = "CREATE TABLE IF NOT EXISTS `lp_users` (`uuid` VARCHAR(36) NOT NULL, `name` VARCHAR(16) NOT NULL, `primary_group` VARCHAR(36) NOT NULL, `perms` TEXT NOT NULL, PRIMARY KEY (`uuid`)) DEFAULT CHARSET=utf8;"; + private static final String CREATETABLE_GROUPS = "CREATE TABLE IF NOT EXISTS `lp_groups` (`name` VARCHAR(36) NOT NULL, `perms` TEXT NULL, PRIMARY KEY (`name`)) DEFAULT CHARSET=utf8;"; + private static final String CREATETABLE_TRACKS = "CREATE TABLE IF NOT EXISTS `lp_tracks` (`name` VARCHAR(36) NOT NULL, `groups` TEXT NULL, PRIMARY KEY (`name`)) DEFAULT CHARSET=utf8;"; + private static final String CREATETABLE_ACTION = "CREATE TABLE IF NOT EXISTS `lp_actions` (`id` INT AUTO_INCREMENT NOT NULL, `time` BIGINT NOT NULL, `actor_uuid` VARCHAR(36) NOT NULL, `actor_name` VARCHAR(16) NOT NULL, `type` CHAR(1) NOT NULL, `acted_uuid` VARCHAR(36) NOT NULL, `acted_name` VARCHAR(36) NOT NULL, `action` VARCHAR(256) NOT NULL, PRIMARY KEY (`id`)) DEFAULT CHARSET=utf8;"; + + private final File file; + private Connection connection = null; + + public H2Datastore(LuckPermsPlugin plugin, File file) { + super(plugin, "H2"); + this.file = file; + } + + @Override + public void init() { + if (!setupTables(CREATETABLE_UUID, CREATETABLE_USERS, CREATETABLE_GROUPS, CREATETABLE_TRACKS, CREATETABLE_ACTION)) { + plugin.getLog().severe("Error occurred whilst initialising the database."); + shutdown(); + } else { + setAcceptingLogins(true); + } + } + + @Override + boolean runQuery(QueryPS queryPS) { + boolean success = false; + try { + Connection connection = getConnection(); + if (connection == null || connection.isClosed()) { + throw new IllegalStateException("SQL connection is null"); + } + + @Cleanup PreparedStatement preparedStatement = connection.prepareStatement(queryPS.getQuery()); + queryPS.onRun(preparedStatement); + preparedStatement.execute(); + success = true; + } catch (SQLException e) { + e.printStackTrace(); + } + return success; + } + + @Override + boolean runQuery(QueryRS queryRS) { + boolean success = false; + try { + Connection connection = getConnection(); + if (connection == null || connection.isClosed()) { + throw new IllegalStateException("SQL connection is null"); + } + + @Cleanup PreparedStatement preparedStatement = connection.prepareStatement(queryRS.getQuery()); + queryRS.onRun(preparedStatement); + preparedStatement.execute(); + + @Cleanup ResultSet resultSet = preparedStatement.executeQuery(); + success = queryRS.onResult(resultSet); + } catch (SQLException e) { + e.printStackTrace(); + } + return success; + } + + @Override + public void shutdown() { + try { + if (connection != null && !connection.isClosed()) { + connection.close(); + } + } catch (SQLException e) { + e.printStackTrace(); + } + } + + @Override + Connection getConnection() throws SQLException { + if (connection == null || connection.isClosed()) { + try { + Class.forName("org.h2.Driver"); + } catch (ClassNotFoundException ignored) {} + + connection = DriverManager.getConnection("jdbc:h2:" + file.getAbsolutePath()); + } + + return connection; + } +} diff --git a/common/src/main/java/me/lucko/luckperms/storage/methods/MongoDBDatastore.java b/common/src/main/java/me/lucko/luckperms/storage/methods/MongoDBDatastore.java new file mode 100644 index 000000000..8fb0ba102 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/storage/methods/MongoDBDatastore.java @@ -0,0 +1,453 @@ +/* + * Copyright (c) 2016 Lucko (Luck) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.storage.methods; + +import com.mongodb.MongoClient; +import com.mongodb.MongoCredential; +import com.mongodb.ServerAddress; +import com.mongodb.client.MongoCollection; +import com.mongodb.client.MongoCursor; +import com.mongodb.client.MongoDatabase; +import com.mongodb.client.model.InsertOneOptions; +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.groups.GroupManager; +import me.lucko.luckperms.storage.Datastore; +import me.lucko.luckperms.storage.DatastoreConfiguration; +import me.lucko.luckperms.tracks.Track; +import me.lucko.luckperms.tracks.TrackManager; +import me.lucko.luckperms.users.User; +import org.bson.Document; + +import java.util.*; +import java.util.concurrent.Callable; +import java.util.stream.Collectors; + +@SuppressWarnings("unchecked") +public class MongoDBDatastore extends Datastore { + + private final DatastoreConfiguration configuration; + private MongoClient mongoClient; + private MongoDatabase database; + + public MongoDBDatastore(LuckPermsPlugin plugin, DatastoreConfiguration configuration) { + super(plugin, "MongoDB"); + this.configuration = configuration; + } + + @Override + public void init() { + MongoCredential credential = MongoCredential.createCredential( + configuration.getUsername(), + configuration.getDatabase(), + configuration.getPassword().toCharArray() + ); + + ServerAddress address = new ServerAddress( + configuration.getAddress().split(":")[0], + Integer.parseInt(configuration.getAddress().split(":")[1]) + ); + + mongoClient = new MongoClient(address, Collections.singletonList(credential)); + database = mongoClient.getDatabase(configuration.getDatabase()); + setAcceptingLogins(true); + } + + @Override + public void shutdown() { + if (mongoClient != null) { + mongoClient.close(); + } + } + + @Override + public boolean logAction(LogEntry entry) { + return call(() -> { + MongoCollection c = database.getCollection("action"); + + Document doc = new Document() + .append("timestamp", entry.getTimestamp()) + .append("actor", entry.getActor()) + .append("actorName", entry.getActorName()) + .append("type", Character.toString(entry.getType())) + .append("actedName", entry.getActedName()) + .append("action", entry.getAction()); + + if (entry.getActed() != null) { + doc.append("acted", entry.getActed()); + } + + c.insertOne(doc, new InsertOneOptions()); + return true; + }, false); + } + + @Override + public Log getLog() { + return call(() -> { + final Log.Builder log = Log.builder(); + MongoCollection c = database.getCollection("action"); + + try (MongoCursor cursor = c.find().iterator()) { + while (cursor.hasNext()) { + Document d = cursor.next(); + + UUID actedUuid = null; + if (d.containsKey("acted")) { + actedUuid = d.get("acted", UUID.class); + } + + LogEntry e = new LogEntry( + d.getLong("timestamp"), + d.get("actor", UUID.class), + d.getString("actorName"), + d.getString("type").toCharArray()[0], + actedUuid, + d.getString("actedName"), + d.getString("action") + ); + log.add(e); + } + } + + return log.build(); + }, null); + } + + @Override + public boolean loadOrCreateUser(UUID uuid, String username) { + User user = plugin.getUserManager().make(uuid, username); + boolean success = call(() -> { + MongoCollection c = database.getCollection("users"); + + try (MongoCursor cursor = c.find(new Document("_id", user.getUuid())).iterator()) { + if (!cursor.hasNext()) { + plugin.getUserManager().giveDefaults(user); + c.insertOne(fromUser(user)); + } else { + Document d = cursor.next(); + user.setPrimaryGroup(d.getString("primaryGroup")); + user.setNodes(revert((Map) d.get("perms"))); + + if (!d.getString("name").equals(user.getName())) { + c.replaceOne(new Document("_id", user.getUuid()), fromUser(user)); + } + } + } + return true; + }, false); + + if (success) plugin.getUserManager().updateOrSet(user); + return success; + } + + @Override + public boolean loadUser(UUID uuid) { + User user = plugin.getUserManager().make(uuid); + boolean success = call(() -> { + MongoCollection c = database.getCollection("users"); + + try (MongoCursor cursor = c.find(new Document("_id", user.getUuid())).iterator()) { + if (cursor.hasNext()) { + Document d = cursor.next(); + user.setName(d.getString("name")); + user.setPrimaryGroup(d.getString("primaryGroup")); + user.setNodes(revert((Map) d.get("perms"))); + return true; + } + return false; + } + }, false); + + if (success) plugin.getUserManager().updateOrSet(user); + return success; + } + + @Override + public boolean saveUser(User user) { + return call(() -> { + MongoCollection c = database.getCollection("users"); + c.replaceOne(new Document("_id", user.getUuid()), fromUser(user)); + return true; + }, false); + } + + @Override + public boolean createAndLoadGroup(String name) { + Group group = plugin.getGroupManager().make(name); + boolean success = call(() -> { + MongoCollection c = database.getCollection("groups"); + + try (MongoCursor cursor = c.find(new Document("_id", group.getName())).iterator()) { + if (!cursor.hasNext()) { + c.insertOne(fromGroup(group)); + } else { + Document d = cursor.next(); + group.setNodes(revert((Map) d.get("perms"))); + } + } + return true; + }, false); + + if (success) plugin.getGroupManager().updateOrSet(group); + return success; + } + + @Override + public boolean loadGroup(String name) { + Group group = plugin.getGroupManager().make(name); + boolean success = call(() -> { + MongoCollection c = database.getCollection("groups"); + + try (MongoCursor cursor = c.find(new Document("_id", group.getName())).iterator()) { + if (cursor.hasNext()) { + Document d = cursor.next(); + group.setNodes(revert((Map) d.get("perms"))); + return true; + } + return false; + } + }, false); + + if (success) plugin.getGroupManager().updateOrSet(group); + return success; + } + + @Override + public boolean loadAllGroups() { + List groups = new ArrayList<>(); + boolean success = call(() -> { + MongoCollection c = database.getCollection("groups"); + + try (MongoCursor cursor = c.find().iterator()) { + while (cursor.hasNext()) { + Document d = cursor.next(); + Group group = plugin.getGroupManager().make(d.getString("_id")); + group.setNodes(revert((Map) d.get("perms"))); + groups.add(group); + } + } + + return true; + }, false); + + if (success) { + GroupManager gm = plugin.getGroupManager(); + gm.unloadAll(); + groups.forEach(gm::set); + } + return success; + } + + @Override + public boolean saveGroup(Group group) { + return call(() -> { + MongoCollection c = database.getCollection("groups"); + return c.replaceOne(new Document("_id", group.getName()), fromGroup(group)).wasAcknowledged(); + }, false); + } + + @Override + public boolean deleteGroup(Group group) { + boolean success = call(() -> { + MongoCollection c = database.getCollection("groups"); + return c.deleteOne(new Document("_id", group.getName())).wasAcknowledged(); + }, false); + + if (success) plugin.getGroupManager().unload(group); + return success; + } + + @Override + public boolean createAndLoadTrack(String name) { + Track track = plugin.getTrackManager().make(name); + boolean success = call(() -> { + MongoCollection c = database.getCollection("tracks"); + + try (MongoCursor cursor = c.find(new Document("_id", track.getName())).iterator()) { + if (!cursor.hasNext()) { + c.insertOne(fromTrack(track)); + } else { + Document d = cursor.next(); + track.setGroups((List) d.get("groups")); + } + } + return true; + }, false); + + if (success) plugin.getTrackManager().updateOrSet(track); + return success; + } + + @Override + public boolean loadTrack(String name) { + Track track = plugin.getTrackManager().make(name); + boolean success = call(() -> { + MongoCollection c = database.getCollection("tracks"); + + try (MongoCursor cursor = c.find(new Document("_id", track.getName())).iterator()) { + if (cursor.hasNext()) { + Document d = cursor.next(); + track.setGroups((List) d.get("groups")); + return true; + } + return false; + } + }, false); + + if (success) plugin.getTrackManager().updateOrSet(track); + return success; + } + + @Override + public boolean loadAllTracks() { + List tracks = new ArrayList<>(); + boolean success = call(() -> { + MongoCollection c = database.getCollection("tracks"); + + try (MongoCursor cursor = c.find().iterator()) { + while (cursor.hasNext()) { + Document d = cursor.next(); + Track track = plugin.getTrackManager().make(d.getString("_id")); + track.setGroups((List) d.get("groups")); + tracks.add(track); + } + } + + return true; + }, false); + + if (success) { + TrackManager tm = plugin.getTrackManager(); + tm.unloadAll(); + tracks.forEach(tm::set); + } + return success; + } + + @Override + public boolean saveTrack(Track track) { + return call(() -> { + MongoCollection c = database.getCollection("tracks"); + return c.replaceOne(new Document("_id", track.getName()), fromTrack(track)).wasAcknowledged(); + }, false); + } + + @Override + public boolean deleteTrack(Track track) { + boolean success = call(() -> { + MongoCollection c = database.getCollection("tracks"); + return c.deleteOne(new Document("_id", track.getName())).wasAcknowledged(); + }, false); + + if (success) plugin.getTrackManager().unload(track); + return success; + } + + @Override + public boolean saveUUIDData(String username, UUID uuid) { + return call(() -> { + MongoCollection c = database.getCollection("uuid"); + + try (MongoCursor cursor = c.find(new Document("_id", uuid)).iterator()) { + if (cursor.hasNext()) { + c.replaceOne(new Document("_id", uuid), new Document("_id", uuid).append("name", username.toLowerCase())); + } else { + c.insertOne(new Document("_id", uuid).append("name", username.toLowerCase())); + } + } + + return true; + }, false); + } + + @Override + public UUID getUUID(String username) { + return call(() -> { + MongoCollection c = database.getCollection("uuid"); + + try (MongoCursor cursor = c.find(new Document("name", username.toLowerCase())).iterator()) { + if (cursor.hasNext()) { + return cursor.next().get("_id", UUID.class); + } + } + return null; + }, null); + } + + private static T call(Callable c, T def) { + try { + return c.call(); + } catch (Exception e) { + e.printStackTrace(); + return def; + } + } + + /* MongoDB does not allow '.' or '$' in key names. + See: https://docs.mongodb.com/manual/reference/limits/#Restrictions-on-Field-Names + The following two methods convert the node maps so they can be stored. */ + private static Map convert(Map map) { + return map.entrySet().stream() + .map(e -> new AbstractMap.SimpleEntry<>(e.getKey().replace(".", "[**DOT**]").replace("$", "[**DOLLAR**]"), e.getValue())) + .collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue)); + } + + private static Map revert(Map map) { + return map.entrySet().stream() + .map(e -> new AbstractMap.SimpleEntry<>(e.getKey().replace("[**DOT**]", ".").replace("[**DOLLAR**]", "$"), e.getValue())) + .collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue)); + } + + private static Document fromUser(User user) { + Document main = new Document("_id", user.getUuid()) + .append("name", user.getName()) + .append("primaryGroup", user.getPrimaryGroup()); + + Document perms = new Document(); + for (Map.Entry e : convert(user.getNodes()).entrySet()) { + perms.append(e.getKey(), e.getValue()); + } + + main.append("perms", perms); + return main; + } + + private static Document fromGroup(Group group) { + Document main = new Document("_id", group.getName()); + + Document perms = new Document(); + for (Map.Entry e : convert(group.getNodes()).entrySet()) { + perms.append(e.getKey(), e.getValue()); + } + + main.append("perms", perms); + return main; + } + + private static Document fromTrack(Track track) { + return new Document("_id", track.getName()).append("groups", track.getGroups()); + } +} diff --git a/common/src/main/java/me/lucko/luckperms/storage/methods/MySQLDatastore.java b/common/src/main/java/me/lucko/luckperms/storage/methods/MySQLDatastore.java index cc0ea80e1..42f67a563 100644 --- a/common/src/main/java/me/lucko/luckperms/storage/methods/MySQLDatastore.java +++ b/common/src/main/java/me/lucko/luckperms/storage/methods/MySQLDatastore.java @@ -25,7 +25,7 @@ package me.lucko.luckperms.storage.methods; import com.zaxxer.hikari.HikariDataSource; import lombok.Cleanup; import me.lucko.luckperms.LuckPermsPlugin; -import me.lucko.luckperms.storage.MySQLConfiguration; +import me.lucko.luckperms.storage.DatastoreConfiguration; import java.sql.Connection; import java.sql.PreparedStatement; @@ -40,10 +40,10 @@ public class MySQLDatastore extends SQLDatastore { private static final String CREATETABLE_TRACKS = "CREATE TABLE IF NOT EXISTS `lp_tracks` (`name` VARCHAR(36) NOT NULL, `groups` TEXT NULL, PRIMARY KEY (`name`)) DEFAULT CHARSET=utf8;"; private static final String CREATETABLE_ACTION = "CREATE TABLE IF NOT EXISTS `lp_actions` (`id` INT AUTO_INCREMENT NOT NULL, `time` BIGINT NOT NULL, `actor_uuid` VARCHAR(36) NOT NULL, `actor_name` VARCHAR(16) NOT NULL, `type` CHAR(1) NOT NULL, `acted_uuid` VARCHAR(36) NOT NULL, `acted_name` VARCHAR(36) NOT NULL, `action` VARCHAR(256) NOT NULL, PRIMARY KEY (`id`)) DEFAULT CHARSET=utf8;"; - private final MySQLConfiguration configuration; + private final DatastoreConfiguration configuration; private HikariDataSource hikari; - public MySQLDatastore(LuckPermsPlugin plugin, MySQLConfiguration configuration) { + public MySQLDatastore(LuckPermsPlugin plugin, DatastoreConfiguration configuration) { super(plugin, "MySQL"); this.configuration = configuration; } diff --git a/common/src/main/java/me/lucko/luckperms/storage/methods/SQLDatastore.java b/common/src/main/java/me/lucko/luckperms/storage/methods/SQLDatastore.java index c6547e3b1..497a346ff 100644 --- a/common/src/main/java/me/lucko/luckperms/storage/methods/SQLDatastore.java +++ b/common/src/main/java/me/lucko/luckperms/storage/methods/SQLDatastore.java @@ -109,7 +109,7 @@ abstract class SQLDatastore extends Datastore { boolean onResult(ResultSet resultSet) throws SQLException { if (resultSet.next()) { user.setName(resultSet.getString("name")); - user.getNodes().putAll(gson.fromJson(resultSet.getString("perms"), NM_TYPE)); + user.setNodes(gson.fromJson(resultSet.getString("perms"), NM_TYPE)); user.setPrimaryGroup(resultSet.getString("primary_group")); return true; } @@ -193,7 +193,7 @@ abstract class SQLDatastore extends Datastore { } }); } else { - user.getNodes().putAll(gson.fromJson(resultSet.getString("perms"), NM_TYPE)); + user.setNodes(gson.fromJson(resultSet.getString("perms"), NM_TYPE)); user.setPrimaryGroup(resultSet.getString("primary_group")); if (!resultSet.getString("name").equals(user.getName())) { @@ -251,7 +251,7 @@ abstract class SQLDatastore extends Datastore { } }); } else { - group.getNodes().putAll(gson.fromJson(resultSet.getString("perms"), NM_TYPE)); + group.setNodes(gson.fromJson(resultSet.getString("perms"), NM_TYPE)); } return success; } @@ -273,7 +273,7 @@ abstract class SQLDatastore extends Datastore { @Override boolean onResult(ResultSet resultSet) throws SQLException { if (resultSet.next()) { - group.getNodes().putAll(gson.fromJson(resultSet.getString("perms"), NM_TYPE)); + group.setNodes(gson.fromJson(resultSet.getString("perms"), NM_TYPE)); return true; } return false; @@ -297,7 +297,7 @@ abstract class SQLDatastore extends Datastore { boolean onResult(ResultSet resultSet) throws SQLException { while (resultSet.next()) { Group group = plugin.getGroupManager().make(resultSet.getString("name")); - group.getNodes().putAll(gson.fromJson(resultSet.getString("perms"), NM_TYPE)); + group.setNodes(gson.fromJson(resultSet.getString("perms"), NM_TYPE)); groups.add(group); } return true; diff --git a/pom.xml b/pom.xml index 22c6ab762..e88323ad7 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ me.lucko.luckperms luckperms - 2.3 + 2.4 common api diff --git a/sponge/pom.xml b/sponge/pom.xml index f11bf52ab..95585607d 100644 --- a/sponge/pom.xml +++ b/sponge/pom.xml @@ -5,7 +5,7 @@ luckperms me.lucko.luckperms - 2.3 + 2.4 4.0.0 @@ -43,12 +43,27 @@ shade - true + false + + + org.slf4j:* + org.xerial:* + com.h2database:* + + com.zaxxer.hikari me.lucko.luckperms.lib.hikari + + com.mongodb + me.lucko.luckperms.lib.mongodb + + + org.bson + me.lucko.luckperms.lib.bson + @@ -128,13 +143,6 @@ ${project.version} compile - - - com.zaxxer - HikariCP - 2.4.7 - compile - de.icongmbh.oss.maven.plugins diff --git a/sponge/src/main/java/me/lucko/luckperms/LPSpongePlugin.java b/sponge/src/main/java/me/lucko/luckperms/LPSpongePlugin.java index 779e49fb2..bbad99be5 100644 --- a/sponge/src/main/java/me/lucko/luckperms/LPSpongePlugin.java +++ b/sponge/src/main/java/me/lucko/luckperms/LPSpongePlugin.java @@ -35,9 +35,7 @@ import me.lucko.luckperms.data.Importer; import me.lucko.luckperms.groups.GroupManager; import me.lucko.luckperms.runnables.UpdateTask; import me.lucko.luckperms.storage.Datastore; -import me.lucko.luckperms.storage.methods.FlatfileDatastore; -import me.lucko.luckperms.storage.methods.MySQLDatastore; -import me.lucko.luckperms.storage.methods.SQLiteDatastore; +import me.lucko.luckperms.storage.StorageFactory; import me.lucko.luckperms.tracks.TrackManager; import me.lucko.luckperms.users.SpongeUserManager; import me.lucko.luckperms.users.UserManager; @@ -106,24 +104,7 @@ public class LPSpongePlugin implements LuckPermsPlugin { SpongeCommand commandManager = new SpongeCommand(this); cmdService.register(this, commandManager, "luckperms", "perms", "lp", "permissions", "p", "perm"); - getLog().info("Detecting storage method..."); - final String storageMethod = configuration.getStorageMethod(); - if (storageMethod.equalsIgnoreCase("mysql")) { - getLog().info("Using MySQL as storage method."); - datastore = new MySQLDatastore(this, configuration.getDatabaseValues()); - } else if (storageMethod.equalsIgnoreCase("sqlite")) { - getLog().info("Using SQLite as storage method."); - datastore = new SQLiteDatastore(this, new File(getMainDir(), "luckperms.sqlite")); - } else if (storageMethod.equalsIgnoreCase("flatfile")) { - getLog().info("Using Flatfile (JSON) as storage method."); - datastore = new FlatfileDatastore(this, getMainDir()); - } else { - getLog().severe("Storage method '" + storageMethod + "' was not recognised. Using SQLite as fallback."); - datastore = new SQLiteDatastore(this, new File(getMainDir(), "luckperms.sqlite")); - } - - getLog().info("Initialising datastore..."); - datastore.init(); + datastore = StorageFactory.getDatastore(this, "h2"); getLog().info("Loading internal permission managers..."); uuidCache = new UuidCache(getConfiguration().getOnlineMode()); @@ -196,6 +177,11 @@ public class LPSpongePlugin implements LuckPermsPlugin { return luckPermsDir; } + @Override + public File getDataFolder() { + return getMainDir(); + } + @Override public String getVersion() { return "null"; @@ -221,6 +207,11 @@ public class LPSpongePlugin implements LuckPermsPlugin { return game.getServer().getOnlinePlayers().stream().map(s -> SpongeSenderFactory.get().wrap(s)).collect(Collectors.toList()); } + @Override + public Sender getConsoleSender() { + return SpongeSenderFactory.get().wrap(game.getServer().getConsole()); + } + @Override public List getPossiblePermissions() { Optional p = game.getServiceManager().provide(PermissionService.class); diff --git a/sponge/src/main/resources/luckperms.conf b/sponge/src/main/resources/luckperms.conf index 840d22b72..35b0ef99a 100644 --- a/sponge/src/main/resources/luckperms.conf +++ b/sponge/src/main/resources/luckperms.conf @@ -1,4 +1,9 @@ -# LuckPerms Configuration +############################################################################## +# +------------------------------------------------------------------------+ # +# | LuckPerms Configuration | # +# | https://github.com/lucko/LuckPerms | # +# +------------------------------------------------------------------------+ # +############################################################################## # The name of the server, used for server specific permissions. Set to 'global' to disable. server="global" @@ -38,14 +43,17 @@ apply-regex=true apply-shorthand=true # Which storage method the plugin should use. -# Currently supported: mysql, sqlite, flatfile -# Fill out connection info below if you're using MySQL -storage-method="sqlite" +# Currently supported: mysql, sqlite, h2, flatfile, mongodb +# Fill out connection info below if you're using MySQL or MongoDB +storage-method="h2" -sql: { +data: { address="localhost:3306" database="minecraft" username="root" password="" + + # Set to -1 to disable. If this is the only instance accessing the datastore, you can disable syncing. + # e.g. if you're using sqlite or flatfile, this can be set to -1 to save resources. sync-minutes=3 }