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
-
-
- 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
}