diff --git a/README.md b/README.md index 3627885c2..178502ae5 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # LuckPerms [![Build Status](https://ci.lucko.me/job/LuckPerms/badge/icon)](https://ci.lucko.me/job/LuckPerms/) -A permissions implementation for Bukkit/BungeeCord. +A permissions implementation for Bukkit/Spigot, BungeeCord and Sponge. ## Links * **Development Builds** - @@ -23,11 +23,12 @@ A permissions implementation for Bukkit/BungeeCord. * **Well documented** - API methods have comprehensive Java docs, it's clear what each method does. * **Efficient/lightweight** - maybe? Who knows, it might be. * **Open Sourced, Free...** - you shouldn't have to pay $10+ for a "powerful" permissions plugin. -* **BungeeCord compatible** - permissions, users and groups are synced across Bukkit/BungeeCord instances +* **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) ## Setup -All configuration options are in the **config.yml** file, which is generated automagically when the plugin first starts. +All configuration options are in the **config.yml/luckperms.conf** file, which is generated automagically when the plugin first starts. You can define the settings for per-server permissions, the storage method and credentials within this file. @@ -57,11 +58,20 @@ Temporary permissions are checked each time a user/group is loaded, and when the The only way around this is to decrease the sync interval. ## API -LuckPerms has an extensive API, allowing for easy integration with other projects. To use the Api, you need to obtain an instance of the `LuckPermsApi` interface. This can be done in two ways, (one way on BungeeCord). +LuckPerms has an extensive API, allowing for easy integration with other projects. To use the Api, you need to obtain an instance of the `LuckPermsApi` interface. This can be done in a number of ways. ```java +// On all platforms final LuckPermsApi api = LuckPerms.getApi(); + +// On Bukkit/Spigot final LuckPermsApi api = Bukkit.getServicesManager().getRegistration(LuckPermsApi.class).getProvider(); + +// On Sponge +Optional provider = Sponge.getServiceManager().provide(LuckPermsApi.class); +if (provider.isPresent()) { + final LuckPermsApi api = provider.get(); +} ``` If you want to use LuckPerms in your onEnable method, you need to add the following to your plugins `plugin.yml`. @@ -83,7 +93,7 @@ You can add LuckPerms as a Maven dependency by adding the following to your proj me.lucko.luckperms luckperms-api - 1.4 + 1.5 ```` diff --git a/api/pom.xml b/api/pom.xml index 2c39afd2a..65cd8f3e2 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -5,11 +5,11 @@ luckperms me.lucko.luckperms - 1.4 + 1.5 4.0.0 - 1.4 + 1.5 luckperms-api diff --git a/api/src/main/java/me/lucko/luckperms/api/Logger.java b/api/src/main/java/me/lucko/luckperms/api/Logger.java new file mode 100644 index 000000000..102b5b4b8 --- /dev/null +++ b/api/src/main/java/me/lucko/luckperms/api/Logger.java @@ -0,0 +1,9 @@ +package me.lucko.luckperms.api; + +public interface Logger { + + void info(String s); + void warn(String s); + void severe(String s); + +} diff --git a/api/src/main/java/me/lucko/luckperms/api/LuckPermsApi.java b/api/src/main/java/me/lucko/luckperms/api/LuckPermsApi.java index d0571eaf8..ce3a16cb2 100644 --- a/api/src/main/java/me/lucko/luckperms/api/LuckPermsApi.java +++ b/api/src/main/java/me/lucko/luckperms/api/LuckPermsApi.java @@ -9,6 +9,7 @@ public interface LuckPermsApi { String getVersion(); Datastore getDatastore(); + Logger getLogger(); User getUser(UUID uuid); User getUser(String name); diff --git a/bukkit/pom.xml b/bukkit/pom.xml index b5f52332c..edfaf2bd7 100644 --- a/bukkit/pom.xml +++ b/bukkit/pom.xml @@ -5,12 +5,12 @@ luckperms me.lucko.luckperms - 1.4 + 1.5 4.0.0 luckperms-bukkit - 1.4 + 1.5 jar LuckPerms @@ -72,14 +72,14 @@ me.lucko.luckperms luckperms-common - 1.4 + 1.5 compile me.lucko.luckperms luckperms-api - 1.4 + 1.5 compile diff --git a/bukkit/src/main/java/me/lucko/luckperms/CommandManagerBukkit.java b/bukkit/src/main/java/me/lucko/luckperms/BukkitCommand.java similarity index 90% rename from bukkit/src/main/java/me/lucko/luckperms/CommandManagerBukkit.java rename to bukkit/src/main/java/me/lucko/luckperms/BukkitCommand.java index 8beecf5e8..a88497447 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/CommandManagerBukkit.java +++ b/bukkit/src/main/java/me/lucko/luckperms/BukkitCommand.java @@ -11,8 +11,8 @@ import java.lang.ref.WeakReference; import java.util.Arrays; import java.util.List; -class CommandManagerBukkit extends CommandManager implements CommandExecutor, TabExecutor { - CommandManagerBukkit(LuckPermsPlugin plugin) { +class BukkitCommand extends CommandManager implements CommandExecutor, TabExecutor { + BukkitCommand(LuckPermsPlugin plugin) { super(plugin); } diff --git a/bukkit/src/main/java/me/lucko/luckperms/LPBukkitPlugin.java b/bukkit/src/main/java/me/lucko/luckperms/LPBukkitPlugin.java index 307453d91..cd7acc8cf 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/LPBukkitPlugin.java +++ b/bukkit/src/main/java/me/lucko/luckperms/LPBukkitPlugin.java @@ -1,6 +1,7 @@ package me.lucko.luckperms; import lombok.Getter; +import me.lucko.luckperms.api.Logger; import me.lucko.luckperms.api.LuckPermsApi; import me.lucko.luckperms.api.implementation.ApiProvider; import me.lucko.luckperms.api.vault.VaultHook; @@ -15,6 +16,7 @@ import me.lucko.luckperms.tracks.TrackManager; import me.lucko.luckperms.users.BukkitUserManager; import me.lucko.luckperms.users.UserManager; import me.lucko.luckperms.utils.LPConfiguration; +import me.lucko.luckperms.utils.LogUtil; import me.lucko.luckperms.utils.UuidCache; import org.bukkit.Bukkit; import org.bukkit.command.PluginCommand; @@ -40,7 +42,7 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin { @Override public void onEnable() { - getLogger().info("Loading configuration..."); + getLog().info("Loading configuration..."); configuration = new BukkitConfig(this); // register events @@ -48,40 +50,40 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin { pm.registerEvents(new PlayerListener(this), this); // register commands - getLogger().info("Registering commands..."); - CommandManagerBukkit commandManager = new CommandManagerBukkit(this); + getLog().info("Registering commands..."); + BukkitCommand commandManager = new BukkitCommand(this); PluginCommand main = getServer().getPluginCommand("luckperms"); main.setExecutor(commandManager); main.setTabCompleter(commandManager); main.setAliases(Arrays.asList("perms", "lp", "permissions", "p", "perm")); - getLogger().info("Detecting storage method..."); + getLog().info("Detecting storage method..."); final String storageMethod = configuration.getStorageMethod(); if (storageMethod.equalsIgnoreCase("mysql")) { - getLogger().info("Using MySQL as storage method."); + getLog().info("Using MySQL as storage method."); datastore = new MySQLDatastore(this, configuration.getDatabaseValues()); } else if (storageMethod.equalsIgnoreCase("sqlite")) { - getLogger().info("Using SQLite as storage method."); + getLog().info("Using SQLite as storage method."); datastore = new SQLiteDatastore(this, new File(getDataFolder(), "luckperms.sqlite")); } else if (storageMethod.equalsIgnoreCase("flatfile")) { - getLogger().info("Using Flatfile (JSON) as storage method."); + getLog().info("Using Flatfile (JSON) as storage method."); datastore = new FlatfileDatastore(this, getDataFolder()); } else { - getLogger().severe("Storage method '" + storageMethod + "' was not recognised. Using SQLite as fallback."); + getLog().severe("Storage method '" + storageMethod + "' was not recognised. Using SQLite as fallback."); datastore = new SQLiteDatastore(this, new File(getDataFolder(), "luckperms.sqlite")); } - getLogger().info("Initialising datastore..."); + getLog().info("Initialising datastore..."); datastore.init(); - getLogger().info("Loading internal permission managers..."); + getLog().info("Loading internal permission managers..."); uuidCache = new UuidCache(getConfiguration().getOnlineMode()); userManager = new BukkitUserManager(this); groupManager = new GroupManager(this); trackManager = new TrackManager(); // Run update task to refresh any online users - getLogger().info("Scheduling Update Task to refresh any online users."); + getLog().info("Scheduling Update Task to refresh any online users."); try { new UpdateTask(this).run(); } catch (Exception e) { @@ -95,33 +97,33 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin { } // Provide vault support - getLogger().info("Attempting to hook into Vault..."); + getLog().info("Attempting to hook into Vault..."); try { if (getServer().getPluginManager().isPluginEnabled("Vault")) { VaultHook.hook(this); - getLogger().info("Registered Vault permission & chat hook."); + getLog().info("Registered Vault permission & chat hook."); } else { - getLogger().info("Vault not found."); + getLog().info("Vault not found."); } } catch (Exception e) { - getLogger().severe("Error occurred whilst hooking into Vault."); + getLog().severe("Error occurred whilst hooking into Vault."); e.printStackTrace(); } - getLogger().info("Registering API..."); + getLog().info("Registering API..."); final ApiProvider provider = new ApiProvider(this); LuckPerms.registerProvider(provider); getServer().getServicesManager().register(LuckPermsApi.class, provider, this, ServicePriority.Normal); - getLogger().info("Successfully loaded."); + getLog().info("Successfully loaded."); } @Override public void onDisable() { - getLogger().info("Closing datastore..."); + getLog().info("Closing datastore..."); datastore.shutdown(); - getLogger().info("Unregistering API..."); + getLog().info("Unregistering API..."); LuckPerms.unregisterProvider(); getServer().getServicesManager().unregisterAll(this); } @@ -136,6 +138,11 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin { Bukkit.getScheduler().runTask(this, r); } + @Override + public Logger getLog() { + return LogUtil.wrap(getLogger()); + } + @Override public String getVersion() { return getDescription().getVersion(); diff --git a/bukkit/src/main/java/me/lucko/luckperms/listeners/PlayerListener.java b/bukkit/src/main/java/me/lucko/luckperms/listeners/PlayerListener.java index 903efbdec..6b644310a 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/listeners/PlayerListener.java +++ b/bukkit/src/main/java/me/lucko/luckperms/listeners/PlayerListener.java @@ -46,14 +46,14 @@ public class PlayerListener implements Listener { plugin.getDatastore().loadOrCreateUser(cache.getUUID(e.getUniqueId()), e.getName()); final long time = System.currentTimeMillis() - startTime; if (time >= 1000) { - plugin.getLogger().warning("Processing login for " + e.getName() + " took " + time + "ms."); + plugin.getLog().warn("Processing login for " + e.getName() + " took " + time + "ms."); } } @EventHandler public void onPlayerLogin(PlayerLoginEvent e) { final Player player = e.getPlayer(); - final User user = plugin.getUserManager().getUser(plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId())); + final User user = plugin.getUserManager().getUser(plugin.getUuidCache().getUUID(player.getUniqueId())); if (user == null) { e.disallow(PlayerLoginEvent.Result.KICK_OTHER, KICK_MESSAGE); @@ -74,6 +74,11 @@ public class PlayerListener implements Listener { refreshPlayer(e.getPlayer()); } + @EventHandler + public void onPlayerChangedWorld(PlayerChangedWorldEvent e) { + refreshPlayer(e.getPlayer()); + } + @EventHandler public void onPlayerQuit(PlayerQuitEvent e) { final Player player = e.getPlayer(); @@ -86,11 +91,6 @@ public class PlayerListener implements Listener { plugin.getUserManager().unloadUser(user); } - @EventHandler - public void onPlayerChangedWorld(PlayerChangedWorldEvent e) { - refreshPlayer(e.getPlayer()); - } - private void refreshPlayer(Player p) { final User user = plugin.getUserManager().getUser(plugin.getUuidCache().getUUID(p.getUniqueId())); if (user != null) { diff --git a/bukkit/src/main/java/me/lucko/luckperms/users/BukkitUser.java b/bukkit/src/main/java/me/lucko/luckperms/users/BukkitUser.java index f56078eba..15064cd3c 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/users/BukkitUser.java +++ b/bukkit/src/main/java/me/lucko/luckperms/users/BukkitUser.java @@ -34,7 +34,7 @@ public class BukkitUser extends User { if (player == null) return; if (attachment == null) { - getPlugin().getLogger().warning("User " + getName() + " does not have a permissions attachment defined."); + getPlugin().getLog().warn("User " + getName() + " does not have a permissions attachment defined."); setAttachment(player.addAttachment(plugin)); } diff --git a/bukkit/src/main/resources/config.yml b/bukkit/src/main/resources/config.yml index 01ad7d85d..1d36f2173 100644 --- a/bukkit/src/main/resources/config.yml +++ b/bukkit/src/main/resources/config.yml @@ -16,7 +16,7 @@ include-global: true # 1. You have Spigot servers connected to a BungeeCord proxy, with online-mode set to false, but 'bungeecord' set to true in the spigot.yml # AND 'ip-forward' set to true in the BungeeCord config.yml -# In this case, set online-mode in LuckPerms to true, dispite the server being in offline mode. +# In this case, set online-mode in LuckPerms to true, despite the server being in offline mode. # 2. You are only running one server instance using LuckPerms, (not a network) # In this case, set online-mode to true no matter what is set in server.properties. (we can just fallback to the servers uuid cache) diff --git a/bungee/pom.xml b/bungee/pom.xml index ff6e821f0..6bd35a7a8 100644 --- a/bungee/pom.xml +++ b/bungee/pom.xml @@ -5,12 +5,12 @@ luckperms me.lucko.luckperms - 1.4 + 1.5 4.0.0 luckperms-bungee - 1.4 + 1.5 jar LuckPerms @@ -65,14 +65,14 @@ me.lucko.luckperms luckperms-common - 1.4 + 1.5 compile me.lucko.luckperms luckperms-api - 1.4 + 1.5 compile diff --git a/bungee/src/main/java/me/lucko/luckperms/MainCommand.java b/bungee/src/main/java/me/lucko/luckperms/BungeeCommand.java similarity index 93% rename from bungee/src/main/java/me/lucko/luckperms/MainCommand.java rename to bungee/src/main/java/me/lucko/luckperms/BungeeCommand.java index d726f6a60..6ec83b4dc 100644 --- a/bungee/src/main/java/me/lucko/luckperms/MainCommand.java +++ b/bungee/src/main/java/me/lucko/luckperms/BungeeCommand.java @@ -10,10 +10,10 @@ import net.md_5.bungee.api.plugin.TabExecutor; import java.lang.ref.WeakReference; import java.util.Arrays; -class MainCommand extends Command implements TabExecutor { +class BungeeCommand extends Command implements TabExecutor { private final CommandManager manager; - public MainCommand(CommandManager manager) { + public BungeeCommand(CommandManager manager) { super("luckpermsbungee", null, "bperms", "lpb", "bpermissions", "bp", "bperm"); this.manager = manager; } diff --git a/bungee/src/main/java/me/lucko/luckperms/LPBungeePlugin.java b/bungee/src/main/java/me/lucko/luckperms/LPBungeePlugin.java index 44794719f..e19ba06ef 100644 --- a/bungee/src/main/java/me/lucko/luckperms/LPBungeePlugin.java +++ b/bungee/src/main/java/me/lucko/luckperms/LPBungeePlugin.java @@ -1,6 +1,7 @@ package me.lucko.luckperms; import lombok.Getter; +import me.lucko.luckperms.api.Logger; import me.lucko.luckperms.api.implementation.ApiProvider; import me.lucko.luckperms.commands.CommandManager; import me.lucko.luckperms.data.Datastore; @@ -13,6 +14,7 @@ import me.lucko.luckperms.tracks.TrackManager; import me.lucko.luckperms.users.BungeeUserManager; import me.lucko.luckperms.users.UserManager; import me.lucko.luckperms.utils.LPConfiguration; +import me.lucko.luckperms.utils.LogUtil; import me.lucko.luckperms.utils.UuidCache; import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.plugin.Plugin; @@ -33,43 +35,43 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin { @Override public void onEnable() { - getLogger().info("Loading configuration..."); + getLog().info("Loading configuration..."); configuration = new BungeeConfig(this); // register events getProxy().getPluginManager().registerListener(this, new PlayerListener(this)); // register commands - getLogger().info("Registering commands..."); - getProxy().getPluginManager().registerCommand(this, new MainCommand(new CommandManager(this))); + getLog().info("Registering commands..."); + getProxy().getPluginManager().registerCommand(this, new BungeeCommand(new CommandManager(this))); // disable the default Bungee /perms command so it gets handled by the Bukkit plugin getProxy().getDisabledCommands().add("perms"); - getLogger().info("Detecting storage method..."); + getLog().info("Detecting storage method..."); final String storageMethod = configuration.getStorageMethod(); if (storageMethod.equalsIgnoreCase("mysql")) { - getLogger().info("Using MySQL as storage method."); + getLog().info("Using MySQL as storage method."); datastore = new MySQLDatastore(this, configuration.getDatabaseValues()); } else if (storageMethod.equalsIgnoreCase("flatfile")) { - getLogger().info("Using Flatfile (JSON) as storage method."); + getLog().info("Using Flatfile (JSON) as storage method."); datastore = new FlatfileDatastore(this, getDataFolder()); } else { - getLogger().severe("Storage method '" + storageMethod + "' was not recognised. Using Flatfile as fallback."); + getLog().severe("Storage method '" + storageMethod + "' was not recognised. Using Flatfile as fallback."); datastore = new FlatfileDatastore(this, getDataFolder()); } - getLogger().info("Initialising datastore..."); + getLog().info("Initialising datastore..."); datastore.init(); - getLogger().info("Loading internal permission managers..."); + getLog().info("Loading internal permission managers..."); uuidCache = new UuidCache(getConfiguration().getOnlineMode()); userManager = new BungeeUserManager(this); groupManager = new GroupManager(this); trackManager = new TrackManager(); // Run update task to refresh any online users - getLogger().info("Scheduling Update Task to refresh any online users."); + getLog().info("Scheduling Update Task to refresh any online users."); try { new UpdateTask(this).run(); } catch (Exception e) { @@ -81,21 +83,26 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin { getProxy().getScheduler().schedule(this, new UpdateTask(this), mins, mins, TimeUnit.MINUTES); } - getLogger().info("Registering API..."); + getLog().info("Registering API..."); LuckPerms.registerProvider(new ApiProvider(this)); - getLogger().info("Successfully loaded."); + getLog().info("Successfully loaded."); } @Override public void onDisable() { - getLogger().info("Closing datastore..."); + getLog().info("Closing datastore..."); datastore.shutdown(); - getLogger().info("Unregistering API..."); + getLog().info("Unregistering API..."); LuckPerms.unregisterProvider(); } + @Override + public Logger getLog() { + return LogUtil.wrap(getLogger()); + } + @Override public String getVersion() { return getDescription().getVersion(); diff --git a/bungee/src/main/java/me/lucko/luckperms/listeners/PlayerListener.java b/bungee/src/main/java/me/lucko/luckperms/listeners/PlayerListener.java index 1b7971bfa..1fcc96ccf 100644 --- a/bungee/src/main/java/me/lucko/luckperms/listeners/PlayerListener.java +++ b/bungee/src/main/java/me/lucko/luckperms/listeners/PlayerListener.java @@ -57,7 +57,7 @@ public class PlayerListener implements Listener { plugin.getDatastore().loadOrCreateUser(cache.getUUID(c.getUniqueId()), c.getName()); final long time = System.currentTimeMillis() - startTime; if (time >= 1000) { - plugin.getLogger().warning("Processing login for " + c.getName() + " took " + time + "ms."); + plugin.getLog().warn("Processing login for " + c.getName() + " took " + time + "ms."); } e.completeIntent(plugin); }); diff --git a/bungee/src/main/java/me/lucko/luckperms/users/BungeeUser.java b/bungee/src/main/java/me/lucko/luckperms/users/BungeeUser.java index e9f1cb2e0..4a602ca99 100644 --- a/bungee/src/main/java/me/lucko/luckperms/users/BungeeUser.java +++ b/bungee/src/main/java/me/lucko/luckperms/users/BungeeUser.java @@ -9,7 +9,6 @@ import java.util.Map; import java.util.UUID; public class BungeeUser extends User { - private final LPBungeePlugin plugin; BungeeUser(UUID uuid, LPBungeePlugin plugin) { diff --git a/bungee/src/main/resources/config.yml b/bungee/src/main/resources/config.yml index fdb4d0600..e1d8a6eea 100644 --- a/bungee/src/main/resources/config.yml +++ b/bungee/src/main/resources/config.yml @@ -16,7 +16,7 @@ include-global: false # 1. You have Spigot servers connected to a BungeeCord proxy, with online-mode set to false, but 'bungeecord' set to true in the spigot.yml # AND 'ip-forward' set to true in the BungeeCord config.yml -# In this case, set online-mode in LuckPerms to true, dispite the server being in offline mode. +# In this case, set online-mode in LuckPerms to true, despite the server being in offline mode. # 2. You are only running one server instance using LuckPerms, (not a network) # In this case, set online-mode to true no matter what is set in server.properties. (we can just fallback to the servers uuid cache) diff --git a/common/pom.xml b/common/pom.xml index 11cdab7bd..50362a34b 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -5,11 +5,11 @@ luckperms me.lucko.luckperms - 1.4 + 1.5 4.0.0 - 1.4 + 1.5 luckperms-common @@ -30,7 +30,7 @@ me.lucko.luckperms luckperms-api - 1.4 + 1.5 compile diff --git a/common/src/main/java/me/lucko/luckperms/LuckPermsPlugin.java b/common/src/main/java/me/lucko/luckperms/LuckPermsPlugin.java index 95148f5b7..1e581bf05 100644 --- a/common/src/main/java/me/lucko/luckperms/LuckPermsPlugin.java +++ b/common/src/main/java/me/lucko/luckperms/LuckPermsPlugin.java @@ -1,5 +1,6 @@ package me.lucko.luckperms; +import me.lucko.luckperms.api.Logger; import me.lucko.luckperms.data.Datastore; import me.lucko.luckperms.groups.GroupManager; import me.lucko.luckperms.tracks.TrackManager; @@ -9,7 +10,6 @@ import me.lucko.luckperms.utils.UuidCache; import java.util.List; import java.util.UUID; -import java.util.logging.Logger; public interface LuckPermsPlugin { @@ -47,7 +47,7 @@ public interface LuckPermsPlugin { * Retrieves the {@link Logger} for the plugin * @return the plugin's {@link Logger} */ - Logger getLogger(); + Logger getLog(); /** * Retrieves the {@link UuidCache} for the plugin diff --git a/common/src/main/java/me/lucko/luckperms/api/implementation/ApiProvider.java b/common/src/main/java/me/lucko/luckperms/api/implementation/ApiProvider.java index 52d14331e..26e883907 100644 --- a/common/src/main/java/me/lucko/luckperms/api/implementation/ApiProvider.java +++ b/common/src/main/java/me/lucko/luckperms/api/implementation/ApiProvider.java @@ -33,6 +33,11 @@ public class ApiProvider implements LuckPermsApi { return new DatastoreLink(plugin.getDatastore()); } + @Override + public Logger getLogger() { + return plugin.getLog(); + } + @Override public User getUser(@NonNull UUID uuid) { final me.lucko.luckperms.users.User user = plugin.getUserManager().getUser(uuid); diff --git a/common/src/main/java/me/lucko/luckperms/commands/CommandManager.java b/common/src/main/java/me/lucko/luckperms/commands/CommandManager.java index 834686954..f2888f1eb 100644 --- a/common/src/main/java/me/lucko/luckperms/commands/CommandManager.java +++ b/common/src/main/java/me/lucko/luckperms/commands/CommandManager.java @@ -23,7 +23,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Optional; -import java.util.logging.Level; import java.util.stream.Collectors; public class CommandManager { @@ -162,7 +161,7 @@ public class CommandManager { } private void registerMainCommand(MainCommand command) { - plugin.getLogger().log(Level.INFO, "[CommandManager] Registered main command '" + command.getName() + "'"); + plugin.getLog().info("[CommandManager] Registered main command '" + command.getName() + "'"); mainCommands.add(command); } diff --git a/common/src/main/java/me/lucko/luckperms/data/methods/MySQLDatastore.java b/common/src/main/java/me/lucko/luckperms/data/methods/MySQLDatastore.java index c1ccccf68..0332d0096 100644 --- a/common/src/main/java/me/lucko/luckperms/data/methods/MySQLDatastore.java +++ b/common/src/main/java/me/lucko/luckperms/data/methods/MySQLDatastore.java @@ -1,12 +1,14 @@ package me.lucko.luckperms.data.methods; import com.zaxxer.hikari.HikariDataSource; +import lombok.Cleanup; import me.lucko.luckperms.LuckPermsPlugin; import me.lucko.luckperms.data.MySQLConfiguration; import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; import java.sql.SQLException; -import java.util.logging.Level; public class MySQLDatastore extends SQLDatastore { @@ -41,13 +43,53 @@ public class MySQLDatastore extends SQLDatastore { hikari.addDataSourceProperty("password", password); if (!setupTables(CREATETABLE_UUID, CREATETABLE_USERS, CREATETABLE_GROUPS, CREATETABLE_TRACKS)) { - plugin.getLogger().log(Level.SEVERE, "Error occurred whilst initialising the database. All connections are disallowed."); + plugin.getLog().severe("Error occurred whilst initialising the database. All connections are disallowed."); shutdown(); } else { setAcceptingLogins(true); } } + @Override + boolean runQuery(QueryPS queryPS) { + boolean success = false; + try { + @Cleanup 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 { + @Cleanup 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() { if (hikari != null) { diff --git a/common/src/main/java/me/lucko/luckperms/data/methods/SQLDatastore.java b/common/src/main/java/me/lucko/luckperms/data/methods/SQLDatastore.java index f6812d2b9..e7080ac1f 100644 --- a/common/src/main/java/me/lucko/luckperms/data/methods/SQLDatastore.java +++ b/common/src/main/java/me/lucko/luckperms/data/methods/SQLDatastore.java @@ -3,7 +3,6 @@ package me.lucko.luckperms.data.methods; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import lombok.AllArgsConstructor; -import lombok.Cleanup; import lombok.Getter; import me.lucko.luckperms.LuckPermsPlugin; import me.lucko.luckperms.data.Datastore; @@ -58,43 +57,8 @@ abstract class SQLDatastore extends Datastore { abstract Connection getConnection() throws SQLException; - private boolean runQuery(QueryPS queryPS) { - boolean success = false; - try { - @Cleanup Connection connection = getConnection(); - if (connection == null) { - 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; - } - - private boolean runQuery(QueryRS queryRS) { - boolean success = false; - try { - @Cleanup 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; - } + abstract boolean runQuery(QueryPS queryPS); + abstract boolean runQuery(QueryRS queryRS); boolean setupTables(String... tableQueries) { boolean success = true; @@ -467,14 +431,14 @@ abstract class SQLDatastore extends Datastore { @Getter @AllArgsConstructor - private abstract class QueryPS { + abstract class QueryPS { private final String query; abstract void onRun(PreparedStatement preparedStatement) throws SQLException; } @Getter @AllArgsConstructor - private abstract class QueryRS { + abstract class QueryRS { private final String query; abstract void onRun(PreparedStatement preparedStatement) throws SQLException; abstract boolean onResult(ResultSet resultSet) throws SQLException; diff --git a/common/src/main/java/me/lucko/luckperms/data/methods/SQLiteDatastore.java b/common/src/main/java/me/lucko/luckperms/data/methods/SQLiteDatastore.java index c1b310900..2769b0199 100644 --- a/common/src/main/java/me/lucko/luckperms/data/methods/SQLiteDatastore.java +++ b/common/src/main/java/me/lucko/luckperms/data/methods/SQLiteDatastore.java @@ -1,12 +1,10 @@ package me.lucko.luckperms.data.methods; +import lombok.Cleanup; import me.lucko.luckperms.LuckPermsPlugin; import java.io.File; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.SQLException; -import java.util.logging.Level; +import java.sql.*; public class SQLiteDatastore extends SQLDatastore { @@ -26,13 +24,53 @@ public class SQLiteDatastore extends SQLDatastore { @Override public void init() { if (!setupTables(CREATETABLE_UUID, CREATETABLE_USERS, CREATETABLE_GROUPS, CREATETABLE_TRACKS)) { - plugin.getLogger().log(Level.SEVERE, "Error occurred whilst initialising the database. All connections are disallowed."); + plugin.getLog().severe("Error occurred whilst initialising the database. All connections are disallowed."); 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 { diff --git a/common/src/main/java/me/lucko/luckperms/runnables/UpdateTask.java b/common/src/main/java/me/lucko/luckperms/runnables/UpdateTask.java index 194974e76..4aefc3d91 100644 --- a/common/src/main/java/me/lucko/luckperms/runnables/UpdateTask.java +++ b/common/src/main/java/me/lucko/luckperms/runnables/UpdateTask.java @@ -9,7 +9,7 @@ public class UpdateTask implements Runnable { @Override public void run() { - plugin.getLogger().info("Running update task."); + plugin.getLog().info("Running update task."); // Reload all of the groups plugin.getDatastore().loadAllGroups(); diff --git a/common/src/main/java/me/lucko/luckperms/utils/LPConfiguration.java b/common/src/main/java/me/lucko/luckperms/utils/LPConfiguration.java index 745a5cc3c..0030e5342 100644 --- a/common/src/main/java/me/lucko/luckperms/utils/LPConfiguration.java +++ b/common/src/main/java/me/lucko/luckperms/utils/LPConfiguration.java @@ -22,14 +22,14 @@ public abstract class LPConfiguration { init(); if (Patterns.NON_ALPHA_NUMERIC.matcher(getServer()).find()) { - plugin.getLogger().severe("Server name defined in config.yml contains invalid characters. Server names can " + + plugin.getLog().severe("Server name defined in config.yml contains invalid characters. Server names can " + "only contain alphanumeric characters.\nDefined server name '" + getServer() + "' will be replaced with '" + defaultServerName + "' (the default)"); set("server", defaultServerName); } if (Patterns.NON_ALPHA_NUMERIC.matcher(getDefaultGroupName()).find()) { - plugin.getLogger().severe("Default group defined in config.yml contains invalid characters. Group names can " + + plugin.getLog().severe("Default group defined in config.yml contains invalid characters. Group names can " + "only contain alphanumeric characters.\nDefined default group name '" + getDefaultGroupName() + "' will be replaced with 'default' (the default)"); set("default-group", "default"); diff --git a/common/src/main/java/me/lucko/luckperms/utils/LogUtil.java b/common/src/main/java/me/lucko/luckperms/utils/LogUtil.java new file mode 100644 index 000000000..7f617efb9 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/utils/LogUtil.java @@ -0,0 +1,49 @@ +package me.lucko.luckperms.utils; + +import lombok.experimental.UtilityClass; +import me.lucko.luckperms.api.Logger; + +@UtilityClass +public class LogUtil { + public static Logger wrap(org.slf4j.Logger l) { + return new Logger() { + private final org.slf4j.Logger logger = l; + + @Override + public void info(String s) { + logger.info(s); + } + + @Override + public void warn(String s) { + logger.warn(s); + } + + @Override + public void severe(String s) { + logger.error(s); + } + }; + } + + public static Logger wrap(java.util.logging.Logger l) { + return new Logger() { + private final java.util.logging.Logger logger = l; + + @Override + public void info(String s) { + logger.info(s); + } + + @Override + public void warn(String s) { + logger.warning(s); + } + + @Override + public void severe(String s) { + logger.severe(s); + } + }; + } +} diff --git a/common/src/main/java/me/lucko/luckperms/utils/Patterns.java b/common/src/main/java/me/lucko/luckperms/utils/Patterns.java index dc69e49b7..159a62e36 100644 --- a/common/src/main/java/me/lucko/luckperms/utils/Patterns.java +++ b/common/src/main/java/me/lucko/luckperms/utils/Patterns.java @@ -6,6 +6,7 @@ import java.util.regex.Pattern; @UtilityClass public class Patterns { + public static final Pattern SPACE_SPLIT = Pattern.compile(" "); public static final Pattern SERVER_SPLIT = Pattern.compile("\\/"); public static final Pattern WORLD_SPLIT = Pattern.compile("\\-"); public static final Pattern TEMP_SPLIT = Pattern.compile("\\$"); diff --git a/common/src/main/java/me/lucko/luckperms/utils/PermissionObject.java b/common/src/main/java/me/lucko/luckperms/utils/PermissionObject.java index 73da89043..8cfc2a90b 100644 --- a/common/src/main/java/me/lucko/luckperms/utils/PermissionObject.java +++ b/common/src/main/java/me/lucko/luckperms/utils/PermissionObject.java @@ -580,7 +580,7 @@ public abstract class PermissionObject { if (group != null) { perms.putAll(group.getLocalPermissions(server, excludedGroups)); } else { - plugin.getLogger().warning("Error whilst refreshing the permissions of '" + objectName + "'." + + plugin.getLog().warn("Error whilst refreshing the permissions of '" + objectName + "'." + "\n The group '" + groupName + "' is not loaded."); } } @@ -603,7 +603,7 @@ public abstract class PermissionObject { if (group != null) { perms.putAll(group.getLocalPermissions(server, excludedGroups)); } else { - plugin.getLogger().warning("Error whilst refreshing the permissions of '" + objectName + "'." + + plugin.getLog().warn("Error whilst refreshing the permissions of '" + objectName + "'." + "\n The group '" + groupName + "' is not loaded."); } } diff --git a/pom.xml b/pom.xml index 3e3fdc00e..b46f0d78b 100644 --- a/pom.xml +++ b/pom.xml @@ -7,12 +7,13 @@ me.lucko.luckperms luckperms - 1.4 + 1.5 - bukkit common - bungee api + bukkit + bungee + sponge pom @@ -33,6 +34,10 @@ luck-repo https://repo.lucko.me/ + + sponge-repo + https://repo.spongepowered.org/maven + diff --git a/sponge/pom.xml b/sponge/pom.xml new file mode 100644 index 000000000..88bd373b3 --- /dev/null +++ b/sponge/pom.xml @@ -0,0 +1,79 @@ + + + + luckperms + me.lucko.luckperms + 1.5 + + 4.0.0 + + luckperms-sponge + 1.5 + jar + LuckPerms + + + clean package + LuckPermsSponge + ${basedir}/src/main/java + + + src/main/resources + true + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.5.1 + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-shade-plugin + 2.4.3 + + + package + + shade + + + true + + + + + + + + + + + org.spongepowered + spongeapi + 4.1.0 + provided + + + + me.lucko.luckperms + luckperms-common + 1.5 + compile + + + + me.lucko.luckperms + luckperms-api + 1.5 + compile + + + diff --git a/sponge/src/main/java/me/lucko/luckperms/LPSpongePlugin.java b/sponge/src/main/java/me/lucko/luckperms/LPSpongePlugin.java new file mode 100644 index 000000000..1b93eab24 --- /dev/null +++ b/sponge/src/main/java/me/lucko/luckperms/LPSpongePlugin.java @@ -0,0 +1,178 @@ +package me.lucko.luckperms; + +import com.google.inject.Inject; +import lombok.Getter; +import me.lucko.luckperms.api.LuckPermsApi; +import me.lucko.luckperms.api.implementation.ApiProvider; +import me.lucko.luckperms.data.Datastore; +import me.lucko.luckperms.data.methods.FlatfileDatastore; +import me.lucko.luckperms.data.methods.MySQLDatastore; +import me.lucko.luckperms.data.methods.SQLiteDatastore; +import me.lucko.luckperms.groups.GroupManager; +import me.lucko.luckperms.listeners.PlayerListener; +import me.lucko.luckperms.runnables.UpdateTask; +import me.lucko.luckperms.tracks.TrackManager; +import me.lucko.luckperms.users.SpongeUserManager; +import me.lucko.luckperms.users.UserManager; +import me.lucko.luckperms.utils.LPConfiguration; +import me.lucko.luckperms.utils.LogUtil; +import me.lucko.luckperms.utils.UuidCache; +import org.slf4j.Logger; +import org.spongepowered.api.Game; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.command.CommandManager; +import org.spongepowered.api.config.ConfigDir; +import org.spongepowered.api.entity.living.player.Player; +import org.spongepowered.api.event.Listener; +import org.spongepowered.api.event.game.state.GamePreInitializationEvent; +import org.spongepowered.api.event.game.state.GameStoppingServerEvent; +import org.spongepowered.api.plugin.Plugin; +import org.spongepowered.api.scheduler.Scheduler; + +import java.io.File; +import java.nio.file.Path; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +@Getter +@Plugin(id = "luckperms", name = "LuckPerms", version = LPSpongePlugin.VERSION) +public class LPSpongePlugin implements LuckPermsPlugin { + static final String VERSION = "1.5"; // TODO load this from pom + + @Inject + private Logger logger; + + @Inject + private Game game; + + @Inject + @ConfigDir(sharedRoot = false) + private Path configDir; + + private Scheduler scheduler = Sponge.getScheduler(); + + private LPConfiguration configuration; + private UserManager userManager; + private GroupManager groupManager; + private TrackManager trackManager; + private Datastore datastore; + private UuidCache uuidCache; + + @Listener + public void onEnable(GamePreInitializationEvent event) { + getLog().info("Loading configuration..."); + configuration = new SpongeConfig(this); + + // register events + Sponge.getEventManager().registerListeners(this, new PlayerListener(this)); + + // register commands + getLog().info("Registering commands..."); + CommandManager cmdService = Sponge.getCommandManager(); + cmdService.register(this, new SpongeCommand(this), "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(getStorageDir(), "luckperms.sqlite")); + } else if (storageMethod.equalsIgnoreCase("flatfile")) { + getLog().info("Using Flatfile (JSON) as storage method."); + datastore = new FlatfileDatastore(this, getStorageDir()); + } else { + getLog().severe("Storage method '" + storageMethod + "' was not recognised. Using SQLite as fallback."); + datastore = new SQLiteDatastore(this, new File(getStorageDir(), "luckperms.sqlite")); + } + + getLog().info("Initialising datastore..."); + datastore.init(); + + getLog().info("Loading internal permission managers..."); + uuidCache = new UuidCache(getConfiguration().getOnlineMode()); + userManager = new SpongeUserManager(this); + groupManager = new GroupManager(this); + trackManager = new TrackManager(); + + // Run update task to refresh any online users + getLog().info("Scheduling Update Task to refresh any online users."); + try { + new UpdateTask(this).run(); + } catch (Exception e) { + e.printStackTrace(); + } + + int mins = getConfiguration().getSyncTime(); + if (mins > 0) { + scheduler.createTaskBuilder().async().interval(mins, TimeUnit.MINUTES).execute(new UpdateTask(this)) + .submit(LPSpongePlugin.this); + } + + getLog().info("Registering API..."); + final ApiProvider provider = new ApiProvider(this); + LuckPerms.registerProvider(provider); + Sponge.getServiceManager().setProvider(this, LuckPermsApi.class, provider); + + getLog().info("Successfully loaded."); + } + + @Listener + public void onDisable(GameStoppingServerEvent event) { + getLog().info("Closing datastore..."); + datastore.shutdown(); + + getLog().info("Unregistering API..."); + LuckPerms.unregisterProvider(); + } + + private File getStorageDir() { + File base = configDir.toFile().getParentFile().getParentFile(); + File luckperms = new File(base, "luckperms"); + luckperms.mkdirs(); + return luckperms; + } + + @Override + public me.lucko.luckperms.api.Logger getLog() { + return LogUtil.wrap(getLogger()); + } + + @Override + public String getVersion() { + return VERSION; + } + + @Override + public String getPlayerStatus(UUID uuid) { + return game.getServer().getPlayer(getUuidCache().getExternalUUID(uuid)).isPresent() ? "&aOnline" : "&cOffline"; + } + + @Override + public int getPlayerCount() { + return game.getServer().getOnlinePlayers().size(); + } + + @Override + public List getPlayerList() { + return game.getServer().getOnlinePlayers().stream().map(Player::getName).collect(Collectors.toList()); + } + + @Override + public void runUpdateTask() { + scheduler.createTaskBuilder().async().execute(new UpdateTask(this)).submit(LPSpongePlugin.this); + } + + @Override + public void doAsync(Runnable r) { + scheduler.createTaskBuilder().async().execute(r).submit(LPSpongePlugin.this); + } + + @Override + public void doSync(Runnable r) { + scheduler.createTaskBuilder().execute(r).submit(LPSpongePlugin.this); + } +} diff --git a/sponge/src/main/java/me/lucko/luckperms/SpongeCommand.java b/sponge/src/main/java/me/lucko/luckperms/SpongeCommand.java new file mode 100644 index 000000000..d0a66d045 --- /dev/null +++ b/sponge/src/main/java/me/lucko/luckperms/SpongeCommand.java @@ -0,0 +1,74 @@ +package me.lucko.luckperms; + +import me.lucko.luckperms.commands.CommandManager; +import me.lucko.luckperms.commands.Sender; +import me.lucko.luckperms.utils.Patterns; +import org.spongepowered.api.command.CommandCallable; +import org.spongepowered.api.command.CommandException; +import org.spongepowered.api.command.CommandResult; +import org.spongepowered.api.command.CommandSource; +import org.spongepowered.api.text.Text; +import org.spongepowered.api.text.serializer.TextSerializers; + +import java.lang.ref.WeakReference; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +public class SpongeCommand extends CommandManager implements CommandCallable { + public SpongeCommand(LuckPermsPlugin plugin) { + super(plugin); + } + + @Override + public CommandResult process(CommandSource source, String s) throws CommandException { + onCommand(makeSender(source), "perms", Arrays.asList(Patterns.SPACE_SPLIT.split(s))); + return CommandResult.success(); + } + + @Override + public List getSuggestions(CommandSource source, String s) throws CommandException { + return onTabComplete(makeSender(source), Arrays.asList(Patterns.SPACE_SPLIT.split(s))); + } + + @Override + public boolean testPermission(CommandSource commandSource) { + return true; + } + + @Override + public Optional getShortDescription(CommandSource commandSource) { + return Optional.of(Text.of("LuckPerms main command.")); + } + + @Override + public Optional getHelp(CommandSource commandSource) { + return Optional.of(Text.of("Type /perms for help.")); + } + + @Override + public Text getUsage(CommandSource commandSource) { + return Text.of("/perms"); + } + + private static Sender makeSender(CommandSource source) { + return new Sender() { + final WeakReference cs = new WeakReference<>(source); + + @SuppressWarnings("deprecation") + @Override + public void sendMessage(String s) { + final CommandSource c = cs.get(); + if (c != null) { + c.sendMessage(TextSerializers.LEGACY_FORMATTING_CODE.deserialize(s)); + } + } + + @Override + public boolean hasPermission(String node) { + final CommandSource c = cs.get(); + return c != null && c.hasPermission(node); + } + }; + } +} diff --git a/sponge/src/main/java/me/lucko/luckperms/SpongeConfig.java b/sponge/src/main/java/me/lucko/luckperms/SpongeConfig.java new file mode 100644 index 000000000..4d08a96c8 --- /dev/null +++ b/sponge/src/main/java/me/lucko/luckperms/SpongeConfig.java @@ -0,0 +1,79 @@ +package me.lucko.luckperms; + +import me.lucko.luckperms.utils.LPConfiguration; +import ninja.leaping.configurate.ConfigurationNode; +import ninja.leaping.configurate.commented.CommentedConfigurationNode; +import ninja.leaping.configurate.hocon.HoconConfigurationLoader; +import ninja.leaping.configurate.loader.ConfigurationLoader; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; + +class SpongeConfig extends LPConfiguration { + private ConfigurationNode root; + + SpongeConfig(LPSpongePlugin plugin) { + super(plugin, "global", true, "sqlite"); + } + + @SuppressWarnings("ResultOfMethodCallIgnored") + private Path makeFile(Path file) throws IOException { + File cfg = file.toFile(); + cfg.getParentFile().mkdirs(); + + if (!cfg.exists()) { + try (InputStream is = getPlugin().getClass().getClassLoader().getResourceAsStream("luckperms.conf")) { + Files.copy(is, cfg.toPath()); + } + } + + return cfg.toPath(); + } + + @Override + protected void init() { + try { + ConfigurationLoader loader = HoconConfigurationLoader.builder() + .setPath(makeFile(getPlugin().getConfigDir().resolve("luckperms.conf"))) + .build(); + + root = loader.load(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private ConfigurationNode getNode(String path) { + String[] paths = path.split("\\."); + ConfigurationNode node = root; + + for (String s : paths) { + node = node.getNode(s); + } + + return node; + } + + @Override + protected void set(String path, Object value) { + getNode(path).setValue(value); + } + + @Override + protected String getString(String path, String def) { + return getNode(path).getString(def); + } + + @Override + protected int getInt(String path, int def) { + return getNode(path).getInt(def); + } + + @Override + protected boolean getBoolean(String path, boolean def) { + return getNode(path).getBoolean(def); + } +} diff --git a/sponge/src/main/java/me/lucko/luckperms/listeners/PlayerListener.java b/sponge/src/main/java/me/lucko/luckperms/listeners/PlayerListener.java new file mode 100644 index 000000000..84e6aa9f3 --- /dev/null +++ b/sponge/src/main/java/me/lucko/luckperms/listeners/PlayerListener.java @@ -0,0 +1,107 @@ +package me.lucko.luckperms.listeners; + +import lombok.AllArgsConstructor; +import me.lucko.luckperms.LPSpongePlugin; +import me.lucko.luckperms.commands.Util; +import me.lucko.luckperms.constants.Message; +import me.lucko.luckperms.users.User; +import me.lucko.luckperms.utils.UuidCache; +import org.spongepowered.api.entity.Entity; +import org.spongepowered.api.entity.living.player.Player; +import org.spongepowered.api.event.Listener; +import org.spongepowered.api.event.entity.DisplaceEntityEvent; +import org.spongepowered.api.event.network.ClientConnectionEvent; +import org.spongepowered.api.profile.GameProfile; +import org.spongepowered.api.text.serializer.TextSerializers; + +import java.util.UUID; + +@AllArgsConstructor +public class PlayerListener { + private static final String KICK_MESSAGE = Util.color(Message.PREFIX + "User data could not be loaded. Please contact an administrator."); + private final LPSpongePlugin plugin; + + @Listener + public void onClientAuth(ClientConnectionEvent.Auth e) { + final long startTime = System.currentTimeMillis(); + if (!plugin.getDatastore().isAcceptingLogins()) { + // Datastore is disabled, prevent players from joining the server + // Just don't load their data, they will be kickec at login + return; + } + + final UuidCache cache = plugin.getUuidCache(); + final GameProfile p = e.getProfile(); + final String name = p.getName().get(); + + if (!cache.isOnlineMode()) { + UUID uuid = plugin.getDatastore().getUUID(name); + if (uuid != null) { + cache.addToCache(p.getUniqueId(), uuid); + } else { + // No previous data for this player + cache.addToCache(p.getUniqueId(), p.getUniqueId()); + plugin.getDatastore().saveUUIDData(name, p.getUniqueId(), b -> {}); + } + } else { + // Online mode, no cache needed. This is just for name -> uuid lookup. + plugin.getDatastore().saveUUIDData(name, p.getUniqueId(), b -> {}); + } + + plugin.getDatastore().loadOrCreateUser(cache.getUUID(p.getUniqueId()), name); + final long time = System.currentTimeMillis() - startTime; + if (time >= 1000) { + plugin.getLog().warn("Processing login for " + p.getName() + " took " + time + "ms."); + } + } + + @SuppressWarnings("deprecation") + @Listener + public void onClientLogin(ClientConnectionEvent.Login e) { + final GameProfile player = e.getProfile(); + final User user = plugin.getUserManager().getUser(plugin.getUuidCache().getUUID(player.getUniqueId())); + + if (user == null) { + e.setCancelled(true); + e.setMessage(TextSerializers.LEGACY_FORMATTING_CODE.deserialize(KICK_MESSAGE)); + return; + } + + user.refreshPermissions(); + } + + @Listener + public void onClientJoin(ClientConnectionEvent.Join e) { + // Refresh permissions again + refreshPlayer(e.getTargetEntity()); + } + + @Listener + public void onPlayerTeleport(DisplaceEntityEvent.Teleport e) { + final Entity entity = e.getTargetEntity(); + if (!(entity instanceof Player)){ + return; + } + + refreshPlayer((Player) entity); + } + + @Listener + public void onClientLeave(ClientConnectionEvent.Disconnect e) { + final Player player = e.getTargetEntity(); + final UuidCache cache = plugin.getUuidCache(); + + // Unload the user from memory when they disconnect; + cache.clearCache(player.getUniqueId()); + + final User user = plugin.getUserManager().getUser(cache.getUUID(player.getUniqueId())); + plugin.getUserManager().unloadUser(user); + } + + private void refreshPlayer(Player p) { + final User user = plugin.getUserManager().getUser(plugin.getUuidCache().getUUID(p.getUniqueId())); + if (user != null) { + user.refreshPermissions(); + } + } +} diff --git a/sponge/src/main/java/me/lucko/luckperms/users/SpongeUser.java b/sponge/src/main/java/me/lucko/luckperms/users/SpongeUser.java new file mode 100644 index 000000000..5fdff8448 --- /dev/null +++ b/sponge/src/main/java/me/lucko/luckperms/users/SpongeUser.java @@ -0,0 +1,41 @@ +package me.lucko.luckperms.users; + +import me.lucko.luckperms.LPSpongePlugin; +import org.spongepowered.api.entity.living.player.Player; +import org.spongepowered.api.util.Tristate; + +import java.util.HashSet; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +class SpongeUser extends User { + private final LPSpongePlugin plugin; + + SpongeUser(UUID uuid, LPSpongePlugin plugin) { + super(uuid, plugin); + this.plugin = plugin; + } + + SpongeUser(UUID uuid, String username, LPSpongePlugin plugin) { + super(uuid, username, plugin); + this.plugin = plugin; + } + + @Override + public void refreshPermissions() { + Optional p = plugin.getGame().getServer().getPlayer(plugin.getUuidCache().getExternalUUID(getUuid())); + if (!p.isPresent()) return; + + final Player player = p.get(); + + // Clear existing permissions + player.getSubjectData().clearParents(); + player.getSubjectData().clearPermissions(); + + // Re-add all defined permissions for the user + final String world = player.getWorld().getName(); + Map local = getLocalPermissions(getPlugin().getConfiguration().getServer(), world, null); + local.entrySet().forEach(e -> player.getSubjectData().setPermission(new HashSet<>(), e.getKey(), Tristate.fromBoolean(e.getValue()))); + } +} diff --git a/sponge/src/main/java/me/lucko/luckperms/users/SpongeUserManager.java b/sponge/src/main/java/me/lucko/luckperms/users/SpongeUserManager.java new file mode 100644 index 000000000..fd0b64169 --- /dev/null +++ b/sponge/src/main/java/me/lucko/luckperms/users/SpongeUserManager.java @@ -0,0 +1,52 @@ +package me.lucko.luckperms.users; + +import me.lucko.luckperms.LPSpongePlugin; +import org.spongepowered.api.entity.living.player.Player; + +import java.util.Optional; +import java.util.UUID; + +public class SpongeUserManager extends UserManager { + private final LPSpongePlugin plugin; + + public SpongeUserManager(LPSpongePlugin plugin) { + super(plugin); + this.plugin = plugin; + } + + @Override + public void unloadUser(User user) { + if (user != null) { + Optional p = plugin.getGame().getServer().getPlayer(plugin.getUuidCache().getExternalUUID(user.getUuid())); + if (p.isPresent()) { + p.get().getSubjectData().clearParents(); + p.get().getSubjectData().clearPermissions(); + } + getUsers().remove(user.getUuid()); + } + } + + @Override + public void cleanupUser(User user) { + if (plugin.getGame().getServer().getPlayer(plugin.getUuidCache().getExternalUUID(user.getUuid())).isPresent()) { + unloadUser(user); + } + } + + @Override + public User makeUser(UUID uuid) { + return new SpongeUser(uuid, plugin); + } + + @Override + public User makeUser(UUID uuid, String username) { + return new SpongeUser(uuid, username, plugin); + } + + @Override + public void updateAllUsers() { + plugin.getGame().getServer().getOnlinePlayers().stream() + .map(p -> plugin.getUuidCache().getUUID(p.getUniqueId())) + .forEach(u -> plugin.getDatastore().loadUser(u)); + } +} diff --git a/sponge/src/main/resources/luckperms.conf b/sponge/src/main/resources/luckperms.conf new file mode 100644 index 000000000..8c422bd19 --- /dev/null +++ b/sponge/src/main/resources/luckperms.conf @@ -0,0 +1,36 @@ +# LuckPerms Configuration + +# The name of the server, used for server specific permissions. Set to 'global' to disable. +server="global" + +# The default group assigned to all users on their first join. +default-group="default" + +# If users on this server should have their global permissions/groups applied. +include-global=true + +# If this server is in offline or online mode. +# This setting allows a player to have the same UUID across a network of offline mode/mixed servers. + +# You should generally reflect the setting in server.properties here. Except when... + +# 1. You have Spigot servers connected to a BungeeCord proxy, with online-mode set to false, but 'bungeecord' set to +# true in the spigot.yml AND 'ip-forward' set to true in the BungeeCord config.yml +# In this case, set online-mode in LuckPerms to true, despite the server being in offline mode. + +# 2. You are only running one server instance using LuckPerms, (not a network) In this case, set online-mode to true no +# matter what is set in server.properties. (we can just fallback to the servers uuid cache) +online-mode=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" + +sql: { + address="localhost:3306" + database="minecraft" + username="root" + password="" + sync-minutes=3 +}