Add /lp tree command - closes #175

This commit is contained in:
Luck 2017-02-15 19:21:27 +00:00
parent 4b16be1b75
commit 05f906a759
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
26 changed files with 615 additions and 86 deletions

View File

@ -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<String, Boolean> e : defaultsProvider.getOpDefaults().entrySet()) {
permissionCache.offer(e.getKey());
permissionVault.offer(e.getKey());
}
for (Map.Entry<String, Boolean> e : defaultsProvider.getNonOpDefaults().entrySet()) {
permissionCache.offer(e.getKey());
permissionVault.offer(e.getKey());
}
ImmutableMap<Map.Entry<String, Boolean>, ImmutableMap<String, Boolean>> permissions = childPermissionProvider.getPermissions();
for (Map.Entry<Map.Entry<String, Boolean>, ImmutableMap<String, Boolean>> e : permissions.entrySet()) {
permissionCache.offer(e.getKey().getKey());
permissionVault.offer(e.getKey().getKey());
for (Map.Entry<String, Boolean> 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) {

View File

@ -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<Void> 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);

View File

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

View File

@ -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())

View File

@ -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<T> extends Command<T, Void> {
* ----------------------------------------------------------------------------------
*/
public static List<String> getPermissionTabComplete(List<String> args, PermissionCache cache) {
public static List<String> getPermissionTabComplete(List<String> 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<T> extends Command<T, Void> {
String start = args.get(0).toLowerCase();
List<String> 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<T> extends Command<T, Void> {
return Collections.emptyList();
}
PermissionCache.Node n = root.getChildren().get().get(s);
TreeNode n = root.getChildren().get().get(s);
if (n == null) {
return Collections.emptyList();
}

View File

@ -75,6 +75,6 @@ public class PermissionCheck extends SharedSubCommand {
@Override
public List<String> onTabComplete(LuckPermsPlugin plugin, Sender sender, List<String> args) {
return getPermissionTabComplete(args, plugin.getPermissionCache());
return getPermissionTabComplete(args, plugin.getPermissionVault());
}
}

View File

@ -84,6 +84,6 @@ public class PermissionCheckInherits extends SharedSubCommand {
@Override
public List<String> onTabComplete(LuckPermsPlugin plugin, Sender sender, List<String> args) {
return getPermissionTabComplete(args, plugin.getPermissionCache());
return getPermissionTabComplete(args, plugin.getPermissionVault());
}
}

View File

@ -99,6 +99,6 @@ public class PermissionSet extends SharedSubCommand {
if (!ret.isEmpty()) {
return ret;
}
return getPermissionTabComplete(args, plugin.getPermissionCache());
return getPermissionTabComplete(args, plugin.getPermissionVault());
}
}

View File

@ -114,6 +114,6 @@ public class PermissionSetTemp extends SharedSubCommand {
if (!ret.isEmpty()) {
return ret;
}
return getPermissionTabComplete(args, plugin.getPermissionCache());
return getPermissionTabComplete(args, plugin.getPermissionVault());
}
}

View File

@ -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)),

View File

@ -0,0 +1,134 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* 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<String> 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;
}
}

View File

@ -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";
}

View File

@ -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),

View File

@ -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),

View File

@ -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));

View File

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

View File

@ -0,0 +1,91 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* 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<ImmutableTreeNode> {
private Map<String, ImmutableTreeNode> children = null;
public ImmutableTreeNode(Map<String, ImmutableTreeNode> children) {
if (children != null) {
LinkedHashMap<String, ImmutableTreeNode> 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<Map<String, ImmutableTreeNode>> getChildren() {
return Optional.ofNullable(children);
}
public List<Map.Entry<Integer, String>> getNodeEndings() {
if (children == null) {
return Collections.emptyList();
}
List<Map.Entry<Integer, String>> results = new ArrayList<>();
for (Map.Entry<String, ImmutableTreeNode> 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);
}
}

View File

@ -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<String> 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<String> 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<String, Node> children = null;
private void insert(String permission) {
List<String> parts = DOT_SPLIT.splitToList(permission);
// lazy init
private synchronized Map<String, Node> getChildMap() {
if (children == null) {
children = new ConcurrentHashMap<>();
}
return children;
}
public Optional<Map<String, Node>> getChildren() {
return Optional.ofNullable(children);
TreeNode current = rootNode;
for (String part : parts) {
current = current.getChildMap().computeIfAbsent(part, s -> new TreeNode());
}
}

View File

@ -0,0 +1,63 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* 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<String, TreeNode> children = null;
// lazy init
public synchronized Map<String, TreeNode> getChildMap() {
if (children == null) {
children = new ConcurrentHashMap<>();
}
return children;
}
public Optional<Map<String, TreeNode>> 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())));
}
}
}

View File

@ -0,0 +1,171 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* 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<TreeNode> 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<Map.Entry<String, String>> ret = asTreeList();
ImmutableList.Builder<String> builder = getPasteHeader(version, "none", ret.size());
builder.add("```");
for (Map.Entry<String, String> 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<Map.Entry<String, String>> ret = asTreeList();
ImmutableList.Builder<String> builder = getPasteHeader(version, username, ret.size());
builder.add("```diff");
for (Map.Entry<String, String> 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<String> getPasteHeader(String version, String referenceUser, int size) {
String date = DATE_FORMAT.format(new Date(System.currentTimeMillis()));
String selection = rootPosition.equals(".") ? "any" : "`" + rootPosition + "`";
return ImmutableList.<String>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<TreeNode> findRoot(PermissionVault source) {
TreeNode root = source.getRootNode();
if (rootPosition.equals(".")) {
return Optional.of(root);
}
List<String> parts = Splitter.on('.').omitEmptyStrings().splitToList(rootPosition);
for (String part : parts) {
if (!root.getChildren().isPresent()) {
return Optional.empty();
}
Map<String, TreeNode> branch = root.getChildren().get();
root = branch.get(part);
if (root == null) {
return Optional.empty();
}
}
return Optional.of(root);
}
private List<Map.Entry<String, String>> asTreeList() {
String prefix = rootPosition.equals(".") ? "" : (rootPosition + ".");
List<Map.Entry<String, String>> ret = new ArrayList<>();
for (Map.Entry<Integer, String> 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;
}
}

View File

@ -0,0 +1,57 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* 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);
}
}

View File

@ -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;

View File

@ -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) {

View File

@ -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."

View File

@ -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<Void> 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...");

View File

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