diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/LPBukkitPlugin.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/LPBukkitPlugin.java index dba085afa..5c416f422 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/LPBukkitPlugin.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/LPBukkitPlugin.java @@ -72,9 +72,9 @@ import me.lucko.luckperms.common.storage.StorageType; import me.lucko.luckperms.common.tasks.CacheHousekeepingTask; import me.lucko.luckperms.common.tasks.ExpireTemporaryTask; import me.lucko.luckperms.common.tasks.UpdateTask; +import me.lucko.luckperms.common.treeview.PermissionVault; import me.lucko.luckperms.common.utils.BufferedRequest; import me.lucko.luckperms.common.utils.LoggerImpl; -import me.lucko.luckperms.common.utils.PermissionCache; import org.bukkit.World; import org.bukkit.command.PluginCommand; @@ -126,7 +126,7 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin { private boolean started = false; private DebugHandler debugHandler; private BukkitSenderFactory senderFactory; - private PermissionCache permissionCache; + private PermissionVault permissionVault; @Override public void onEnable() { @@ -139,7 +139,7 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin { ignoringLogs = ConcurrentHashMap.newKeySet(); debugHandler = new DebugHandler(scheduler.getAsyncBukkitExecutor(), getVersion()); - permissionCache = new PermissionCache(scheduler.getAsyncBukkitExecutor()); + permissionVault = new PermissionVault(scheduler.getAsyncBukkitExecutor()); getLog().info("Loading configuration..."); configuration = new BukkitConfig(this); @@ -160,18 +160,18 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin { getServer().getScheduler().runTaskAsynchronously(this, () -> { for (Map.Entry e : defaultsProvider.getOpDefaults().entrySet()) { - permissionCache.offer(e.getKey()); + permissionVault.offer(e.getKey()); } for (Map.Entry e : defaultsProvider.getNonOpDefaults().entrySet()) { - permissionCache.offer(e.getKey()); + permissionVault.offer(e.getKey()); } ImmutableMap, ImmutableMap> permissions = childPermissionProvider.getPermissions(); for (Map.Entry, ImmutableMap> e : permissions.entrySet()) { - permissionCache.offer(e.getKey().getKey()); + permissionVault.offer(e.getKey().getKey()); for (Map.Entry e1 : e.getValue().entrySet()) { - permissionCache.offer(e1.getKey()); + permissionVault.offer(e1.getKey()); } } }); @@ -336,7 +336,7 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin { started = false; defaultsProvider.close(); - permissionCache.setShutdown(true); + permissionVault.setShutdown(true); debugHandler.setShutdown(true); for (Player player : getServer().getOnlinePlayers()) { @@ -398,7 +398,7 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin { updateTaskBuffer = null; debugHandler = null; senderFactory = null; - permissionCache = null; + permissionVault = null; } public void tryVaultHook(boolean force) { diff --git a/bungee/src/main/java/me/lucko/luckperms/bungee/LPBungeePlugin.java b/bungee/src/main/java/me/lucko/luckperms/bungee/LPBungeePlugin.java index 4a6135f0d..9e6c73d94 100644 --- a/bungee/src/main/java/me/lucko/luckperms/bungee/LPBungeePlugin.java +++ b/bungee/src/main/java/me/lucko/luckperms/bungee/LPBungeePlugin.java @@ -64,9 +64,9 @@ import me.lucko.luckperms.common.storage.StorageType; import me.lucko.luckperms.common.tasks.CacheHousekeepingTask; import me.lucko.luckperms.common.tasks.ExpireTemporaryTask; import me.lucko.luckperms.common.tasks.UpdateTask; +import me.lucko.luckperms.common.treeview.PermissionVault; import me.lucko.luckperms.common.utils.BufferedRequest; import me.lucko.luckperms.common.utils.LoggerImpl; -import me.lucko.luckperms.common.utils.PermissionCache; import net.md_5.bungee.api.config.ServerInfo; import net.md_5.bungee.api.connection.ProxiedPlayer; @@ -102,7 +102,7 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin { private BufferedRequest updateTaskBuffer; private DebugHandler debugHandler; private BungeeSenderFactory senderFactory; - private PermissionCache permissionCache; + private PermissionVault permissionVault; @Override public void onEnable() { @@ -112,7 +112,7 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin { log = new LoggerImpl(getConsoleSender()); LuckPermsPlugin.sendStartupBanner(getConsoleSender(), this); debugHandler = new DebugHandler(scheduler.getAsyncExecutor(), getVersion()); - permissionCache = new PermissionCache(scheduler.getAsyncExecutor()); + permissionVault = new PermissionVault(scheduler.getAsyncExecutor()); getLog().info("Loading configuration..."); configuration = new BungeeConfig(this); diff --git a/common/src/main/java/me/lucko/luckperms/common/calculators/PermissionCalculator.java b/common/src/main/java/me/lucko/luckperms/common/calculators/PermissionCalculator.java index 4432ca037..2d883e56a 100644 --- a/common/src/main/java/me/lucko/luckperms/common/calculators/PermissionCalculator.java +++ b/common/src/main/java/me/lucko/luckperms/common/calculators/PermissionCalculator.java @@ -59,7 +59,7 @@ public class PermissionCalculator { permission = permission.toLowerCase(); Tristate t = cache.getUnchecked(permission); plugin.getDebugHandler().offer(objectName, permission, t); - plugin.getPermissionCache().offer(permission); + plugin.getPermissionVault().offer(permission); return t; } diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/CommandManager.java b/common/src/main/java/me/lucko/luckperms/common/commands/CommandManager.java index b77fff944..c77f5c592 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/CommandManager.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/CommandManager.java @@ -40,6 +40,7 @@ import me.lucko.luckperms.common.commands.misc.NetworkSyncCommand; import me.lucko.luckperms.common.commands.misc.ReloadConfigCommand; import me.lucko.luckperms.common.commands.misc.SearchCommand; import me.lucko.luckperms.common.commands.misc.SyncCommand; +import me.lucko.luckperms.common.commands.misc.TreeCommand; import me.lucko.luckperms.common.commands.misc.VerboseCommand; import me.lucko.luckperms.common.commands.sender.Sender; import me.lucko.luckperms.common.commands.track.CreateTrack; @@ -89,6 +90,7 @@ public class CommandManager { .add(new SyncCommand()) .add(new InfoCommand()) .add(new VerboseCommand()) + .add(new TreeCommand()) .add(new SearchCommand()) .add(new CheckCommand()) .add(new NetworkSyncCommand()) diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/SubCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/SubCommand.java index 68c42e643..358d59f80 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/SubCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/SubCommand.java @@ -34,7 +34,8 @@ import me.lucko.luckperms.common.core.model.Group; import me.lucko.luckperms.common.core.model.Track; import me.lucko.luckperms.common.core.model.User; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; -import me.lucko.luckperms.common.utils.PermissionCache; +import me.lucko.luckperms.common.treeview.PermissionVault; +import me.lucko.luckperms.common.treeview.TreeNode; import java.util.ArrayList; import java.util.Arrays; @@ -73,7 +74,7 @@ public abstract class SubCommand extends Command { * ---------------------------------------------------------------------------------- */ - public static List getPermissionTabComplete(List args, PermissionCache cache) { + public static List getPermissionTabComplete(List args, PermissionVault cache) { if (args.size() <= 1) { if (args.isEmpty() || args.get(0).equals("")) { return cache.getRootNode().getChildren() @@ -84,7 +85,7 @@ public abstract class SubCommand extends Command { String start = args.get(0).toLowerCase(); List parts = new ArrayList<>(Splitter.on('.').splitToList(start)); - PermissionCache.Node root = cache.getRootNode(); + TreeNode root = cache.getRootNode(); if (parts.size() <= 1) { if (!root.getChildren().isPresent()) { @@ -101,7 +102,7 @@ public abstract class SubCommand extends Command { return Collections.emptyList(); } - PermissionCache.Node n = root.getChildren().get().get(s); + TreeNode n = root.getChildren().get().get(s); if (n == null) { return Collections.emptyList(); } diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/generic/permission/PermissionCheck.java b/common/src/main/java/me/lucko/luckperms/common/commands/generic/permission/PermissionCheck.java index b5b61ff67..57e05e72d 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/generic/permission/PermissionCheck.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/generic/permission/PermissionCheck.java @@ -75,6 +75,6 @@ public class PermissionCheck extends SharedSubCommand { @Override public List onTabComplete(LuckPermsPlugin plugin, Sender sender, List args) { - return getPermissionTabComplete(args, plugin.getPermissionCache()); + return getPermissionTabComplete(args, plugin.getPermissionVault()); } } diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/generic/permission/PermissionCheckInherits.java b/common/src/main/java/me/lucko/luckperms/common/commands/generic/permission/PermissionCheckInherits.java index 331fa6aa9..8823eb64a 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/generic/permission/PermissionCheckInherits.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/generic/permission/PermissionCheckInherits.java @@ -84,6 +84,6 @@ public class PermissionCheckInherits extends SharedSubCommand { @Override public List onTabComplete(LuckPermsPlugin plugin, Sender sender, List args) { - return getPermissionTabComplete(args, plugin.getPermissionCache()); + return getPermissionTabComplete(args, plugin.getPermissionVault()); } } diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/generic/permission/PermissionSet.java b/common/src/main/java/me/lucko/luckperms/common/commands/generic/permission/PermissionSet.java index 69ed26875..10833594c 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/generic/permission/PermissionSet.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/generic/permission/PermissionSet.java @@ -99,6 +99,6 @@ public class PermissionSet extends SharedSubCommand { if (!ret.isEmpty()) { return ret; } - return getPermissionTabComplete(args, plugin.getPermissionCache()); + return getPermissionTabComplete(args, plugin.getPermissionVault()); } } diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/generic/permission/PermissionSetTemp.java b/common/src/main/java/me/lucko/luckperms/common/commands/generic/permission/PermissionSetTemp.java index 2408ed937..37a74809d 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/generic/permission/PermissionSetTemp.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/generic/permission/PermissionSetTemp.java @@ -114,6 +114,6 @@ public class PermissionSetTemp extends SharedSubCommand { if (!ret.isEmpty()) { return ret; } - return getPermissionTabComplete(args, plugin.getPermissionCache()); + return getPermissionTabComplete(args, plugin.getPermissionVault()); } } diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/misc/InfoCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/misc/InfoCommand.java index ad78fae23..f04cf9e53 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/misc/InfoCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/misc/InfoCommand.java @@ -76,6 +76,7 @@ public class InfoCommand extends SingleCommand { plugin.getLocaleManager().getSize(), plugin.getPreProcessContexts(false).size(), plugin.getContextManager().getCalculatorsSize(), + plugin.getPermissionVault().getSize(), formatBoolean(c.get(ConfigKeys.ONLINE_MODE)), formatBoolean(c.get(ConfigKeys.INCLUDING_GLOBAL_PERMS)), formatBoolean(c.get(ConfigKeys.INCLUDING_GLOBAL_WORLD_PERMS)), diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/misc/TreeCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/misc/TreeCommand.java new file mode 100644 index 000000000..5ed971285 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/commands/misc/TreeCommand.java @@ -0,0 +1,134 @@ +/* + * 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.common.commands.misc; + +import me.lucko.luckperms.api.caching.PermissionData; +import me.lucko.luckperms.common.caching.UserCache; +import me.lucko.luckperms.common.commands.Arg; +import me.lucko.luckperms.common.commands.CommandException; +import me.lucko.luckperms.common.commands.CommandResult; +import me.lucko.luckperms.common.commands.SingleCommand; +import me.lucko.luckperms.common.commands.sender.Sender; +import me.lucko.luckperms.common.commands.utils.ArgumentUtils; +import me.lucko.luckperms.common.commands.utils.Util; +import me.lucko.luckperms.common.constants.Message; +import me.lucko.luckperms.common.constants.Permission; +import me.lucko.luckperms.common.core.model.User; +import me.lucko.luckperms.common.plugin.LuckPermsPlugin; +import me.lucko.luckperms.common.treeview.TreeView; +import me.lucko.luckperms.common.treeview.TreeViewBuilder; +import me.lucko.luckperms.common.utils.Predicates; + +import io.github.mkremins.fanciful.ChatColor; +import io.github.mkremins.fanciful.FancyMessage; + +import java.util.List; +import java.util.UUID; + +public class TreeCommand extends SingleCommand { + public TreeCommand() { + super("Tree", "Generate a tree view of permissions", + "/%s tree [selection] [max level] [player]", Permission.TREE, Predicates.alwaysFalse(), + Arg.list( + Arg.create("selection", false, "the root of the tree. specify \".\" to include all permissions"), + Arg.create("max level", false, "how many branch levels should be returned"), + Arg.create("player", false, "the name of an online player to check against") + ) + ); + } + + @Override + public CommandResult execute(LuckPermsPlugin plugin, Sender sender, List args, String label) throws CommandException { + String selection = "."; + int maxLevel = 5; + String player = null; + + if (args.size() > 0) { + selection = args.get(0); + } + if (args.size() > 1) { + maxLevel = ArgumentUtils.handleIntOrElse(1, args, 5); + } + if (args.size() > 2) { + player = args.get(2); + } + + if (player != null) { + User user; + UUID u = Util.parseUuid(player); + if (u != null) { + user = plugin.getUserManager().get(u); + } else { + user = plugin.getUserManager().getByUsername(player); + } + + if (user == null) { + Message.USER_NOT_ONLINE.send(sender, player); + return CommandResult.STATE_ERROR; + } + + UserCache data = user.getUserData(); + if (data == null) { + Message.USER_NO_DATA.send(sender, user.getName()); + return CommandResult.STATE_ERROR; + } + + PermissionData permissionData = data.getPermissionData(plugin.getContextForUser(user)); + TreeView view = TreeViewBuilder.newBuilder().rootPosition(selection).maxLevels(maxLevel).build(plugin.getPermissionVault()); + + if (!view.hasData()) { + Message.TREE_EMPTY.send(sender); + return CommandResult.FAILURE; + } + + Message.TREE_UPLOAD_START.send(sender); + + String url = view.uploadPasteData(plugin.getVersion(), user.getName(), permissionData); + if (url == null) { + url = "null"; + } + + Message.TREE_URL.send(sender); + sender.sendMessage(new FancyMessage(url).color(ChatColor.getByChar('b')).link(url)); + return CommandResult.SUCCESS; + } + + TreeView view = TreeViewBuilder.newBuilder().rootPosition(selection).maxLevels(maxLevel).build(plugin.getPermissionVault()); + + if (!view.hasData()) { + Message.TREE_EMPTY.send(sender); + return CommandResult.FAILURE; + } + + Message.TREE_UPLOAD_START.send(sender); + + String url = view.uploadPasteData(plugin.getVersion()); + if (url == null) { + url = "null"; + } + + Message.TREE_URL.send(sender); + sender.sendMessage(new FancyMessage(url).color(ChatColor.getByChar('b')).link(url)); + return CommandResult.SUCCESS; + } +} diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/misc/VerboseCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/misc/VerboseCommand.java index dd2fac10d..57a0fcf95 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/misc/VerboseCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/misc/VerboseCommand.java @@ -101,7 +101,7 @@ public class VerboseCommand extends SingleCommand { } else { Message.VERBOSE_RECORDING_UPLOAD_START.send(sender); - String url = listener.uploadPastedData(); + String url = listener.uploadPasteData(); if (url == null) { url = "null"; } diff --git a/common/src/main/java/me/lucko/luckperms/common/constants/Message.java b/common/src/main/java/me/lucko/luckperms/common/constants/Message.java index 6a43dfb75..b54c6e9a5 100644 --- a/common/src/main/java/me/lucko/luckperms/common/constants/Message.java +++ b/common/src/main/java/me/lucko/luckperms/common/constants/Message.java @@ -106,6 +106,10 @@ public enum Message { VERBOSE_RECORDING_UPLOAD_START("&bVerbose recording was disabled. Uploading results...", true), VERBOSE_RECORDING_URL("&aVerbose results URL:", true), + TREE_UPLOAD_START("&bGenerating permission tree...", true), + TREE_EMPTY("&aUnable to generate tree. No results were found.", true), + TREE_URL("&aPermission Tree URL:", true), + SEARCH_SEARCHING("&aSearching for users and groups with &b{0}&a...", true), SEARCH_RESULT("&aFound &b{0}&a entries from &b{1}&a users and &b{2}&a groups.", true), SEARCH_SHOWING_USERS("&bShowing user entries:", true), @@ -164,16 +168,17 @@ public enum Message { "{PREFIX}&f- &3Translations loaded: &a{12}" + "\n" + "{PREFIX}&f- &3Pre-process contexts: &a{13}" + "\n" + "{PREFIX}&f- &3Context Calculators: &a{14}" + "\n" + + "{PREFIX}&f- &3Unique permissions: &a{15}" + "\n" + "{PREFIX}&f- &bConfiguration:" + "\n" + - "{PREFIX}&f- &3Online Mode: {15}" + "\n" + + "{PREFIX}&f- &3Online Mode: {16}" + "\n" + "{PREFIX}&f- &bPermission Calculation:" + "\n" + - "{PREFIX}&f- &3Including Global: {16}" + "\n" + - "{PREFIX}&f- &3Including Global World: {17}" + "\n" + - "{PREFIX}&f- &3Applying Global Groups: {18}" + "\n" + - "{PREFIX}&f- &3Applying Global World Groups: {19}" + "\n" + - "{PREFIX}&f- &3Applying Wildcards: {20}" + "\n" + - "{PREFIX}&f- &3Applying Regex: {21}" + "\n" + - "{PREFIX}&f- &3Applying Shorthand: {22}", + "{PREFIX}&f- &3Including Global: {17}" + "\n" + + "{PREFIX}&f- &3Including Global World: {18}" + "\n" + + "{PREFIX}&f- &3Applying Global Groups: {19}" + "\n" + + "{PREFIX}&f- &3Applying Global World Groups: {20}" + "\n" + + "{PREFIX}&f- &3Applying Wildcards: {21}" + "\n" + + "{PREFIX}&f- &3Applying Regex: {22}" + "\n" + + "{PREFIX}&f- &3Applying Shorthand: {23}", false ), CREATE_GROUP_ERROR("There was an error whilst creating the group.", true), diff --git a/common/src/main/java/me/lucko/luckperms/common/constants/Permission.java b/common/src/main/java/me/lucko/luckperms/common/constants/Permission.java index e092888ac..c6cef5550 100644 --- a/common/src/main/java/me/lucko/luckperms/common/constants/Permission.java +++ b/common/src/main/java/me/lucko/luckperms/common/constants/Permission.java @@ -38,6 +38,7 @@ public enum Permission { SYNC(list("sync"), Type.NONE), INFO(list("info"), Type.NONE), VERBOSE(list("verbose"), Type.NONE), + TREE(list("tree"), Type.NONE), SEARCH(list("search"), Type.NONE), CHECK(list("check"), Type.NONE), IMPORT(list("import"), Type.NONE), diff --git a/common/src/main/java/me/lucko/luckperms/common/debug/DebugListener.java b/common/src/main/java/me/lucko/luckperms/common/debug/DebugListener.java index 12c316465..12506d66b 100644 --- a/common/src/main/java/me/lucko/luckperms/common/debug/DebugListener.java +++ b/common/src/main/java/me/lucko/luckperms/common/debug/DebugListener.java @@ -156,7 +156,7 @@ public class DebugListener { return token.equals(" ") || token.equals("|") || token.equals("&") || token.equals("(") || token.equals(")") || token.equals("!"); } - public String uploadPastedData() { + public String uploadPasteData() { long now = System.currentTimeMillis(); String startDate = DATE_FORMAT.format(new Date(startTime)); String endDate = DATE_FORMAT.format(new Date(now)); diff --git a/common/src/main/java/me/lucko/luckperms/common/plugin/LuckPermsPlugin.java b/common/src/main/java/me/lucko/luckperms/common/plugin/LuckPermsPlugin.java index 4690fcb14..7d8849047 100644 --- a/common/src/main/java/me/lucko/luckperms/common/plugin/LuckPermsPlugin.java +++ b/common/src/main/java/me/lucko/luckperms/common/plugin/LuckPermsPlugin.java @@ -44,8 +44,8 @@ import me.lucko.luckperms.common.managers.TrackManager; import me.lucko.luckperms.common.managers.UserManager; import me.lucko.luckperms.common.messaging.InternalMessagingService; import me.lucko.luckperms.common.storage.Storage; +import me.lucko.luckperms.common.treeview.PermissionVault; import me.lucko.luckperms.common.utils.BufferedRequest; -import me.lucko.luckperms.common.utils.PermissionCache; import java.io.File; import java.io.InputStream; @@ -173,7 +173,7 @@ public interface LuckPermsPlugin { * * @return the permission cache instance */ - PermissionCache getPermissionCache(); + PermissionVault getPermissionVault(); /** * Gets the LuckPerms Scheduler instance diff --git a/common/src/main/java/me/lucko/luckperms/common/treeview/ImmutableTreeNode.java b/common/src/main/java/me/lucko/luckperms/common/treeview/ImmutableTreeNode.java new file mode 100644 index 000000000..5688ce4af --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/treeview/ImmutableTreeNode.java @@ -0,0 +1,91 @@ +/* + * 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.common.treeview; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * An immutable and sorted version of TreeNode + * + * Entries in the children map are sorted first by whether they have any children, and then alphabetically + */ +public class ImmutableTreeNode implements Comparable { + private Map children = null; + + public ImmutableTreeNode(Map children) { + if (children != null) { + LinkedHashMap sortedMap = children.entrySet().stream() + .sorted((o1, o2) -> { + int childStatus = o1.getValue().compareTo(o2.getValue()); + if (childStatus != 0) { + return childStatus; + } + + return String.CASE_INSENSITIVE_ORDER.compare(o1.getKey(), o2.getKey()); + }) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new)); + + this.children = ImmutableMap.copyOf(sortedMap); + } + } + + public Optional> getChildren() { + return Optional.ofNullable(children); + } + + public List> getNodeEndings() { + if (children == null) { + return Collections.emptyList(); + } + + List> results = new ArrayList<>(); + for (Map.Entry node : children.entrySet()) { + + // add self + results.add(Maps.immutableEntry(0, node.getKey())); + + // add child nodes, incrementing their level & appending their prefix node + results.addAll(node.getValue().getNodeEndings().stream() + .map(e -> Maps.immutableEntry( + e.getKey() + 1, // increment level + node.getKey() + "." + e.getValue()) + ) + .collect(Collectors.toList())); + } + return results; + } + + @Override + public int compareTo(ImmutableTreeNode o) { + return (children != null) == o.getChildren().isPresent() ? 0 : (children != null ? 1 : -1); + } +} diff --git a/common/src/main/java/me/lucko/luckperms/common/utils/PermissionCache.java b/common/src/main/java/me/lucko/luckperms/common/treeview/PermissionVault.java similarity index 63% rename from common/src/main/java/me/lucko/luckperms/common/utils/PermissionCache.java rename to common/src/main/java/me/lucko/luckperms/common/treeview/PermissionVault.java index 63a6c0849..065c8407a 100644 --- a/common/src/main/java/me/lucko/luckperms/common/utils/PermissionCache.java +++ b/common/src/main/java/me/lucko/luckperms/common/treeview/PermissionVault.java @@ -20,7 +20,7 @@ * SOFTWARE. */ -package me.lucko.luckperms.common.utils; +package me.lucko.luckperms.common.treeview; import lombok.Getter; import lombok.NonNull; @@ -29,69 +29,65 @@ import lombok.Setter; import com.google.common.base.Splitter; import java.util.List; -import java.util.Map; -import java.util.Optional; import java.util.Queue; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.Executor; -public class PermissionCache { +/** + * Stores a collection of all permissions known to the platform. + */ +public class PermissionVault implements Runnable { + private static final Splitter DOT_SPLIT = Splitter.on('.').omitEmptyStrings(); @Getter - private final Node rootNode; + private final TreeNode rootNode; private final Queue queue; @Setter private boolean shutdown = false; - public PermissionCache(Executor executor) { - rootNode = new Node(); + public PermissionVault(Executor executor) { + rootNode = new TreeNode(); queue = new ConcurrentLinkedQueue<>(); - executor.execute(() -> { - while (true) { - for (String e; (e = queue.poll()) != null; ) { - insert(e.toLowerCase()); - } - - if (shutdown) { - return; - } + executor.execute(this); + } + @Override + public void run() { + while (true) { + for (String e; (e = queue.poll()) != null; ) { try { - Thread.sleep(5000); - } catch (InterruptedException ignored) {} + insert(e.toLowerCase()); + } catch (Exception ex) { + ex.printStackTrace(); + } } - }); + + if (shutdown) { + return; + } + + try { + Thread.sleep(5000); + } catch (InterruptedException ignored) {} + } } public void offer(@NonNull String permission) { queue.offer(permission); } - private void insert(String permission) { - List parts = Splitter.on('.').splitToList(permission); - - Node current = rootNode; - for (String part : parts) { - current = current.getChildMap().computeIfAbsent(part, s -> new Node()); - } + public int getSize() { + return rootNode.getDeepSize(); } - public static class Node { - private Map children = null; + private void insert(String permission) { + List parts = DOT_SPLIT.splitToList(permission); - // lazy init - private synchronized Map getChildMap() { - if (children == null) { - children = new ConcurrentHashMap<>(); - } - return children; - } - - public Optional> getChildren() { - return Optional.ofNullable(children); + TreeNode current = rootNode; + for (String part : parts) { + current = current.getChildMap().computeIfAbsent(part, s -> new TreeNode()); } } diff --git a/common/src/main/java/me/lucko/luckperms/common/treeview/TreeNode.java b/common/src/main/java/me/lucko/luckperms/common/treeview/TreeNode.java new file mode 100644 index 000000000..c7542c0eb --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/treeview/TreeNode.java @@ -0,0 +1,63 @@ +/* + * 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.common.treeview; + +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +/** + * Represents one "branch" of the node tree + */ +public class TreeNode { + private Map children = null; + + // lazy init + public synchronized Map getChildMap() { + if (children == null) { + children = new ConcurrentHashMap<>(); + } + return children; + } + + public Optional> getChildren() { + return Optional.ofNullable(children); + } + + public int getDeepSize() { + if (children == null) { + return 1; + } else { + return children.values().stream().mapToInt(TreeNode::getDeepSize).sum(); + } + } + + public ImmutableTreeNode makeImmutableCopy() { + if (children == null) { + return new ImmutableTreeNode(null); + } else { + return new ImmutableTreeNode(children.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().makeImmutableCopy()))); + } + } +} diff --git a/common/src/main/java/me/lucko/luckperms/common/treeview/TreeView.java b/common/src/main/java/me/lucko/luckperms/common/treeview/TreeView.java new file mode 100644 index 000000000..61bcdc625 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/treeview/TreeView.java @@ -0,0 +1,171 @@ +/* + * 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.common.treeview; + +import com.google.common.base.Splitter; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Maps; + +import me.lucko.luckperms.api.Tristate; +import me.lucko.luckperms.api.caching.PermissionData; +import me.lucko.luckperms.common.utils.PasteUtils; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +public class TreeView { + private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z"); + + private final String rootPosition; + private final int maxLevels; + + private final ImmutableTreeNode view; + + public TreeView(PermissionVault source, String rootPosition, int maxLevels) { + this.rootPosition = rootPosition; + this.maxLevels = maxLevels; + + Optional root = findRoot(source); + this.view = root.map(TreeNode::makeImmutableCopy).orElse(null); + } + + public boolean hasData() { + return view != null; + } + + public String uploadPasteData(String version) { + if (!hasData()) { + throw new IllegalStateException(); + } + + List> ret = asTreeList(); + ImmutableList.Builder builder = getPasteHeader(version, "none", ret.size()); + builder.add("```"); + + for (Map.Entry e : ret) { + builder.add(e.getKey() + e.getValue()); + } + + builder.add("```"); + ret.clear(); + + return PasteUtils.paste("luckperms-tree.md", "LuckPerms Permission Tree", builder.build().stream().collect(Collectors.joining("\n"))); + } + + public String uploadPasteData(String version, String username, PermissionData checker) { + if (!hasData()) { + throw new IllegalStateException(); + } + + List> ret = asTreeList(); + ImmutableList.Builder builder = getPasteHeader(version, username, ret.size()); + builder.add("```diff"); + + for (Map.Entry e : ret) { + Tristate tristate = checker.getPermissionValue(e.getValue()); + builder.add(getTristateDiffPrefix(tristate) + e.getKey() + e.getValue()); + } + + builder.add("```"); + ret.clear(); + + return PasteUtils.paste("luckperms-tree.md", "LuckPerms Permission Tree", builder.build().stream().collect(Collectors.joining("\n"))); + } + + private static String getTristateDiffPrefix(Tristate t) { + switch (t) { + case TRUE: + return "+ "; + case FALSE: + return "- "; + default: + return "# "; + } + } + + private ImmutableList.Builder getPasteHeader(String version, String referenceUser, int size) { + String date = DATE_FORMAT.format(new Date(System.currentTimeMillis())); + String selection = rootPosition.equals(".") ? "any" : "`" + rootPosition + "`"; + + return ImmutableList.builder() + .add("## Permission Tree") + .add("#### This file was automatically generated by [LuckPerms](https://github.com/lucko/LuckPerms) v" + version) + .add("") + .add("### Metadata") + .add("| Selection | Max Recursion | Reference User | Size | Produced at |") + .add("|-----------|---------------|----------------|------|-------------|") + .add("| " + selection + " | " + maxLevels + " | " + referenceUser + " | **" + size + "** | " + date + " |") + .add("") + .add("### Output"); + } + + private Optional findRoot(PermissionVault source) { + TreeNode root = source.getRootNode(); + + if (rootPosition.equals(".")) { + return Optional.of(root); + } + + List parts = Splitter.on('.').omitEmptyStrings().splitToList(rootPosition); + for (String part : parts) { + + if (!root.getChildren().isPresent()) { + return Optional.empty(); + } + + Map branch = root.getChildren().get(); + + root = branch.get(part); + if (root == null) { + return Optional.empty(); + } + } + + return Optional.of(root); + } + + private List> asTreeList() { + String prefix = rootPosition.equals(".") ? "" : (rootPosition + "."); + List> ret = new ArrayList<>(); + + for (Map.Entry s : view.getNodeEndings()) { + if (s.getKey() > maxLevels) { + continue; + } + + String treeStructure = Strings.repeat("│ ", s.getKey()) + "├── "; + String node = prefix + s.getValue(); + + ret.add(Maps.immutableEntry(treeStructure, node)); + } + + return ret; + } + +} diff --git a/common/src/main/java/me/lucko/luckperms/common/treeview/TreeViewBuilder.java b/common/src/main/java/me/lucko/luckperms/common/treeview/TreeViewBuilder.java new file mode 100644 index 000000000..fcf956146 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/treeview/TreeViewBuilder.java @@ -0,0 +1,57 @@ +/* + * 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.common.treeview; + +import lombok.Setter; +import lombok.experimental.Accessors; + +@Accessors(fluent = true) +public class TreeViewBuilder { + public static TreeViewBuilder newBuilder() { + return new TreeViewBuilder(); + } + + @Setter + private String rootPosition; + @Setter + private int maxLevels; + + private TreeViewBuilder() { + this.rootPosition = "."; + this.maxLevels = 5; + } + + public TreeView build(PermissionVault source) { + if (maxLevels < 1) { + maxLevels = 1; + } + if (rootPosition.equals("") || rootPosition.equals("*")) { + rootPosition = "."; + } else if (!rootPosition.equals(".") && rootPosition.endsWith(".")) { + rootPosition = rootPosition.substring(0, rootPosition.length() - 1); + } + + return new TreeView(source, rootPosition, maxLevels); + } + +} diff --git a/common/src/main/java/me/lucko/luckperms/common/utils/ExtractedContexts.java b/common/src/main/java/me/lucko/luckperms/common/utils/ExtractedContexts.java index 2a9ef1bc1..25e819728 100644 --- a/common/src/main/java/me/lucko/luckperms/common/utils/ExtractedContexts.java +++ b/common/src/main/java/me/lucko/luckperms/common/utils/ExtractedContexts.java @@ -27,6 +27,7 @@ import lombok.ToString; import me.lucko.luckperms.api.Contexts; import me.lucko.luckperms.api.context.ContextSet; +import me.lucko.luckperms.api.context.ImmutableContextSet; import me.lucko.luckperms.api.context.MutableContextSet; @Getter @@ -41,7 +42,7 @@ public class ExtractedContexts { } private Contexts contexts; - private ContextSet contextSet; + private ImmutableContextSet contextSet; private String server; private String world; diff --git a/common/src/main/java/me/lucko/luckperms/common/utils/PasteUtils.java b/common/src/main/java/me/lucko/luckperms/common/utils/PasteUtils.java index 8be958100..9e8a150c5 100644 --- a/common/src/main/java/me/lucko/luckperms/common/utils/PasteUtils.java +++ b/common/src/main/java/me/lucko/luckperms/common/utils/PasteUtils.java @@ -32,6 +32,7 @@ import java.io.OutputStream; import java.io.StringWriter; import java.net.HttpURLConnection; import java.net.URL; +import java.nio.charset.StandardCharsets; public class PasteUtils { @@ -54,7 +55,7 @@ public class PasteUtils { .endObject() .endObject(); - os.write(sw.toString().getBytes()); + os.write(sw.toString().getBytes(StandardCharsets.UTF_8)); } if (connection.getResponseCode() >= 400) { diff --git a/default-lang.yml b/default-lang.yml index ca2cf4af9..801d569ff 100644 --- a/default-lang.yml +++ b/default-lang.yml @@ -60,6 +60,10 @@ verbose-recording-on-query: "&bVerbose recording set to &aTRUE &bfor permissions verbose-recording-upload-start: "&bVerbose recording was disabled. Uploading results..." verbose-recording-url: "&aVerbose results URL:" +tree-upload-start: "&bGenerating permission tree..." +tree-empty: "&aUnable to generate tree. No results were found." +tree-url: "&aPermission Tree URL:" + search-searching: "&aSearching for users and groups with &b{0}&a..." search-result: "&aFound &b{0}&a entries from &b{1}&a users and &b{2}&a groups." search-showing-users: "&bShowing user entries:" @@ -118,16 +122,17 @@ info: > {PREFIX}&f- &3Translations loaded: &a{12}\n {PREFIX}&f- &3Pre-process contexts: &a{13}\n {PREFIX}&f- &3Context Calculators: &a{14}\n + {PREFIX}&f- &3Unique permissions: &a{15}\n {PREFIX}&f- &bConfiguration:\n - {PREFIX}&f- &3Online Mode: {15}\n + {PREFIX}&f- &3Online Mode: {16}\n {PREFIX}&f- &bPermission Calculation:\n - {PREFIX}&f- &3Including Global: {16}\n - {PREFIX}&f- &3Including Global World: {17}\n - {PREFIX}&f- &3Applying Global Groups: {18}\n - {PREFIX}&f- &3Applying Global World Groups: {19}\n - {PREFIX}&f- &3Applying Wildcards: {20}\n - {PREFIX}&f- &3Applying Regex: {21}\n - {PREFIX}&f- &3Applying Shorthand: {22} + {PREFIX}&f- &3Including Global: {17}\n + {PREFIX}&f- &3Including Global World: {18}\n + {PREFIX}&f- &3Applying Global Groups: {19}\n + {PREFIX}&f- &3Applying Global World Groups: {20}\n + {PREFIX}&f- &3Applying Wildcards: {21}\n + {PREFIX}&f- &3Applying Regex: {22}\n + {PREFIX}&f- &3Applying Shorthand: {23} create-group-error: "There was an error whilst creating the group." delete-group-error: "There was an error whilst deleting the group." delete-group-error-default: "You cannot delete the default group." diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/LPSpongePlugin.java b/sponge/src/main/java/me/lucko/luckperms/sponge/LPSpongePlugin.java index dbc797a3e..71d3cbb58 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/LPSpongePlugin.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/LPSpongePlugin.java @@ -60,9 +60,9 @@ import me.lucko.luckperms.common.storage.StorageType; import me.lucko.luckperms.common.tasks.CacheHousekeepingTask; import me.lucko.luckperms.common.tasks.ExpireTemporaryTask; import me.lucko.luckperms.common.tasks.UpdateTask; +import me.lucko.luckperms.common.treeview.PermissionVault; import me.lucko.luckperms.common.utils.BufferedRequest; import me.lucko.luckperms.common.utils.LoggerImpl; -import me.lucko.luckperms.common.utils.PermissionCache; import me.lucko.luckperms.sponge.commands.SpongeMainCommand; import me.lucko.luckperms.sponge.contexts.WorldCalculator; import me.lucko.luckperms.sponge.managers.SpongeGroupManager; @@ -158,7 +158,7 @@ public class LPSpongePlugin implements LuckPermsPlugin { private BufferedRequest updateTaskBuffer; private DebugHandler debugHandler; private SpongeSenderFactory senderFactory; - private PermissionCache permissionCache; + private PermissionVault permissionVault; @Listener(order = Order.FIRST) public void onEnable(GamePreInitializationEvent event) { @@ -168,7 +168,7 @@ public class LPSpongePlugin implements LuckPermsPlugin { log = new LoggerImpl(getConsoleSender()); LuckPermsPlugin.sendStartupBanner(getConsoleSender(), this); debugHandler = new DebugHandler(scheduler.getAsyncExecutor(), getVersion()); - permissionCache = new PermissionCache(scheduler.getAsyncExecutor()); + permissionVault = new PermissionVault(scheduler.getAsyncExecutor()); timings = new LPTimings(this); getLog().info("Loading configuration..."); diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsService.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsService.java index 698ab14d3..66ebaf651 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsService.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsService.java @@ -355,7 +355,7 @@ public class LuckPermsService implements PermissionService { subject.getTransientSubjectData().setPermission(ContextSet.empty(), id, assignment.getValue()); } - service.getPlugin().getPermissionCache().offer(id); + service.getPlugin().getPermissionVault().offer(id); return d; }