mirror of
https://github.com/LuckPerms/LuckPerms.git
synced 2024-11-30 22:53:27 +01:00
Cleanup verbose & treeview packages. Return known permissions from the PermissionVault as Sponge PermissionDescriptions
This commit is contained in:
parent
1d5e3205ac
commit
d98b464ce9
@ -64,7 +64,7 @@ public class PermissionCalculator {
|
|||||||
Tristate result = lookupCache.get(permission);
|
Tristate result = lookupCache.get(permission);
|
||||||
|
|
||||||
// log this permission lookup to the verbose handler
|
// log this permission lookup to the verbose handler
|
||||||
plugin.getVerboseHandler().offer(objectName, permission, result);
|
plugin.getVerboseHandler().offerCheckData(objectName, permission, result);
|
||||||
|
|
||||||
// return the result
|
// return the result
|
||||||
return result;
|
return result;
|
||||||
|
@ -36,6 +36,7 @@ import me.lucko.luckperms.common.locale.LocaleManager;
|
|||||||
import me.lucko.luckperms.common.locale.Message;
|
import me.lucko.luckperms.common.locale.Message;
|
||||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||||
import me.lucko.luckperms.common.utils.Predicates;
|
import me.lucko.luckperms.common.utils.Predicates;
|
||||||
|
import me.lucko.luckperms.common.verbose.VerboseFilter;
|
||||||
import me.lucko.luckperms.common.verbose.VerboseListener;
|
import me.lucko.luckperms.common.verbose.VerboseListener;
|
||||||
|
|
||||||
import net.kyori.text.Component;
|
import net.kyori.text.Component;
|
||||||
@ -71,14 +72,14 @@ public class VerboseCommand extends SingleCommand {
|
|||||||
|
|
||||||
String filter = filters.isEmpty() ? "" : filters.stream().collect(Collectors.joining(" "));
|
String filter = filters.isEmpty() ? "" : filters.stream().collect(Collectors.joining(" "));
|
||||||
|
|
||||||
if (!VerboseListener.isValidFilter(filter)) {
|
if (!VerboseFilter.isValidFilter(filter)) {
|
||||||
Message.VERBOSE_INVALID_FILTER.send(sender, filter);
|
Message.VERBOSE_INVALID_FILTER.send(sender, filter);
|
||||||
return CommandResult.FAILURE;
|
return CommandResult.FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean notify = !mode.equals("record");
|
boolean notify = !mode.equals("record");
|
||||||
|
|
||||||
plugin.getVerboseHandler().register(sender, filter, notify);
|
plugin.getVerboseHandler().registerListener(sender, filter, notify);
|
||||||
|
|
||||||
if (notify) {
|
if (notify) {
|
||||||
if (!filter.equals("")) {
|
if (!filter.equals("")) {
|
||||||
@ -98,7 +99,7 @@ public class VerboseCommand extends SingleCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (mode.equals("off") || mode.equals("false") || mode.equals("paste")) {
|
if (mode.equals("off") || mode.equals("false") || mode.equals("paste")) {
|
||||||
VerboseListener listener = plugin.getVerboseHandler().unregister(sender.getUuid());
|
VerboseListener listener = plugin.getVerboseHandler().unregisterListener(sender.getUuid());
|
||||||
|
|
||||||
if (mode.equals("paste")) {
|
if (mode.equals("paste")) {
|
||||||
if (listener == null) {
|
if (listener == null) {
|
||||||
|
@ -136,4 +136,9 @@ public final class AbstractSender<T> implements Sender {
|
|||||||
return this.uuid.equals(Constants.IMPORT_UUID);
|
return this.uuid.equals(Constants.IMPORT_UUID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValid() {
|
||||||
|
return ref.get() != null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -112,4 +112,11 @@ public interface Sender {
|
|||||||
*/
|
*/
|
||||||
boolean isImport();
|
boolean isImport();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets whether this sender is still valid & receiving messages.
|
||||||
|
*
|
||||||
|
* @return if this sender is valid
|
||||||
|
*/
|
||||||
|
boolean isValid();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,6 @@ import me.lucko.luckperms.common.commands.sender.Sender;
|
|||||||
import me.lucko.luckperms.common.commands.utils.Util;
|
import me.lucko.luckperms.common.commands.utils.Util;
|
||||||
import me.lucko.luckperms.common.locale.Message;
|
import me.lucko.luckperms.common.locale.Message;
|
||||||
import me.lucko.luckperms.common.utils.DateUtil;
|
import me.lucko.luckperms.common.utils.DateUtil;
|
||||||
import me.lucko.luckperms.common.utils.FakeSender;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -58,7 +57,7 @@ public class Importer implements Runnable {
|
|||||||
private final Set<Sender> notify;
|
private final Set<Sender> notify;
|
||||||
private final List<String> commands;
|
private final List<String> commands;
|
||||||
private final Map<Integer, Result> cmdResult;
|
private final Map<Integer, Result> cmdResult;
|
||||||
private final FakeSender fake;
|
private final ImporterSender fake;
|
||||||
|
|
||||||
private long lastMsg = 0;
|
private long lastMsg = 0;
|
||||||
private int executing = -1;
|
private int executing = -1;
|
||||||
@ -82,7 +81,7 @@ public class Importer implements Runnable {
|
|||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
this.cmdResult = new HashMap<>();
|
this.cmdResult = new HashMap<>();
|
||||||
this.fake = new FakeSender(commandManager.getPlugin(), this::logMessage);
|
this.fake = new ImporterSender(commandManager.getPlugin(), this::logMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
* SOFTWARE.
|
* SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package me.lucko.luckperms.common.utils;
|
package me.lucko.luckperms.common.data;
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ import java.util.UUID;
|
|||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class FakeSender implements Sender {
|
public class ImporterSender implements Sender {
|
||||||
private final LuckPermsPlugin plugin;
|
private final LuckPermsPlugin plugin;
|
||||||
private final Consumer<String> messageConsumer;
|
private final Consumer<String> messageConsumer;
|
||||||
|
|
||||||
@ -93,4 +93,9 @@ public class FakeSender implements Sender {
|
|||||||
public boolean isImport() {
|
public boolean isImport() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValid() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
@ -35,24 +35,28 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An immutable and sorted version of TreeNode
|
* An immutable and sorted version of TreeNode
|
||||||
*
|
*
|
||||||
* Entries in the children map are sorted first by whether they have any children, and then alphabetically
|
* Entries in the children map are sorted first by whether they have
|
||||||
|
* any children, and then alphabetically
|
||||||
*/
|
*/
|
||||||
public class ImmutableTreeNode implements Comparable<ImmutableTreeNode> {
|
public class ImmutableTreeNode implements Comparable<ImmutableTreeNode> {
|
||||||
private Map<String, ImmutableTreeNode> children = null;
|
private Map<String, ImmutableTreeNode> children = null;
|
||||||
|
|
||||||
public ImmutableTreeNode(Map<String, ImmutableTreeNode> children) {
|
public ImmutableTreeNode(Stream<Map.Entry<String, ImmutableTreeNode>> children) {
|
||||||
if (children != null) {
|
if (children != null) {
|
||||||
LinkedHashMap<String, ImmutableTreeNode> sortedMap = children.entrySet().stream()
|
LinkedHashMap<String, ImmutableTreeNode> sortedMap = children
|
||||||
.sorted((o1, o2) -> {
|
.sorted((o1, o2) -> {
|
||||||
|
// sort first by if the node has any children
|
||||||
int childStatus = o1.getValue().compareTo(o2.getValue());
|
int childStatus = o1.getValue().compareTo(o2.getValue());
|
||||||
if (childStatus != 0) {
|
if (childStatus != 0) {
|
||||||
return childStatus;
|
return childStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// then alphabetically
|
||||||
return String.CASE_INSENSITIVE_ORDER.compare(o1.getKey(), o2.getKey());
|
return String.CASE_INSENSITIVE_ORDER.compare(o1.getKey(), o2.getKey());
|
||||||
})
|
})
|
||||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
|
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
|
||||||
@ -65,6 +69,13 @@ public class ImmutableTreeNode implements Comparable<ImmutableTreeNode> {
|
|||||||
return Optional.ofNullable(children);
|
return Optional.ofNullable(children);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the node endings of each branch of the tree at this stage
|
||||||
|
*
|
||||||
|
* The key represents the depth of the node.
|
||||||
|
*
|
||||||
|
* @return the node endings
|
||||||
|
*/
|
||||||
public List<Map.Entry<Integer, String>> getNodeEndings() {
|
public List<Map.Entry<Integer, String>> getNodeEndings() {
|
||||||
if (children == null) {
|
if (children == null) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
@ -80,6 +91,7 @@ public class ImmutableTreeNode implements Comparable<ImmutableTreeNode> {
|
|||||||
results.addAll(node.getValue().getNodeEndings().stream()
|
results.addAll(node.getValue().getNodeEndings().stream()
|
||||||
.map(e -> Maps.immutableEntry(
|
.map(e -> Maps.immutableEntry(
|
||||||
e.getKey() + 1, // increment level
|
e.getKey() + 1, // increment level
|
||||||
|
// add this node's key infront of the child value
|
||||||
node.getKey() + "." + e.getValue())
|
node.getKey() + "." + e.getValue())
|
||||||
)
|
)
|
||||||
.collect(Collectors.toList()));
|
.collect(Collectors.toList()));
|
||||||
|
@ -30,9 +30,12 @@ import lombok.NonNull;
|
|||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
import com.google.common.base.Splitter;
|
import com.google.common.base.Splitter;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
@ -42,8 +45,14 @@ import java.util.concurrent.Executor;
|
|||||||
public class PermissionVault implements Runnable {
|
public class PermissionVault implements Runnable {
|
||||||
private static final Splitter DOT_SPLIT = Splitter.on('.').omitEmptyStrings();
|
private static final Splitter DOT_SPLIT = Splitter.on('.').omitEmptyStrings();
|
||||||
|
|
||||||
|
// the root node in the tree
|
||||||
@Getter
|
@Getter
|
||||||
private final TreeNode rootNode;
|
private final TreeNode rootNode;
|
||||||
|
|
||||||
|
// the known permissions already in the vault
|
||||||
|
private final Set<String> knownPermissions;
|
||||||
|
|
||||||
|
// a queue of permission strings to be processed by the tree
|
||||||
private final Queue<String> queue;
|
private final Queue<String> queue;
|
||||||
|
|
||||||
@Setter
|
@Setter
|
||||||
@ -51,6 +60,7 @@ public class PermissionVault implements Runnable {
|
|||||||
|
|
||||||
public PermissionVault(Executor executor) {
|
public PermissionVault(Executor executor) {
|
||||||
rootNode = new TreeNode();
|
rootNode = new TreeNode();
|
||||||
|
knownPermissions = ConcurrentHashMap.newKeySet(3000);
|
||||||
queue = new ConcurrentLinkedQueue<>();
|
queue = new ConcurrentLinkedQueue<>();
|
||||||
|
|
||||||
executor.execute(this);
|
executor.execute(this);
|
||||||
@ -61,7 +71,10 @@ public class PermissionVault implements Runnable {
|
|||||||
while (true) {
|
while (true) {
|
||||||
for (String e; (e = queue.poll()) != null; ) {
|
for (String e; (e = queue.poll()) != null; ) {
|
||||||
try {
|
try {
|
||||||
insert(e.toLowerCase());
|
String s = e.toLowerCase();
|
||||||
|
if (knownPermissions.add(s)) {
|
||||||
|
insert(s);
|
||||||
|
}
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
}
|
}
|
||||||
@ -81,13 +94,19 @@ public class PermissionVault implements Runnable {
|
|||||||
queue.offer(permission);
|
queue.offer(permission);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Set<String> getKnownPermissions() {
|
||||||
|
return ImmutableSet.copyOf(knownPermissions);
|
||||||
|
}
|
||||||
|
|
||||||
public int getSize() {
|
public int getSize() {
|
||||||
return rootNode.getDeepSize();
|
return rootNode.getDeepSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void insert(String permission) {
|
private void insert(String permission) {
|
||||||
|
// split the permission up into parts
|
||||||
List<String> parts = DOT_SPLIT.splitToList(permission);
|
List<String> parts = DOT_SPLIT.splitToList(permission);
|
||||||
|
|
||||||
|
// insert the permission into the node structure
|
||||||
TreeNode current = rootNode;
|
TreeNode current = rootNode;
|
||||||
for (String part : parts) {
|
for (String part : parts) {
|
||||||
current = current.getChildMap().computeIfAbsent(part, s -> new TreeNode());
|
current = current.getChildMap().computeIfAbsent(part, s -> new TreeNode());
|
||||||
|
@ -25,13 +25,14 @@
|
|||||||
|
|
||||||
package me.lucko.luckperms.common.treeview;
|
package me.lucko.luckperms.common.treeview;
|
||||||
|
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents one "branch" of the node tree
|
* Represents one "branch" or "level" of the node tree
|
||||||
*/
|
*/
|
||||||
public class TreeNode {
|
public class TreeNode {
|
||||||
private Map<String, TreeNode> children = null;
|
private Map<String, TreeNode> children = null;
|
||||||
@ -60,7 +61,12 @@ public class TreeNode {
|
|||||||
if (children == null) {
|
if (children == null) {
|
||||||
return new ImmutableTreeNode(null);
|
return new ImmutableTreeNode(null);
|
||||||
} else {
|
} else {
|
||||||
return new ImmutableTreeNode(children.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().makeImmutableCopy())));
|
return new ImmutableTreeNode(children.entrySet().stream()
|
||||||
|
.map(e -> Maps.immutableEntry(
|
||||||
|
e.getKey(),
|
||||||
|
e.getValue().makeImmutableCopy()
|
||||||
|
))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,62 +42,188 @@ import java.util.Map;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A readable view of a branch of {@link TreeNode}s.
|
||||||
|
*/
|
||||||
public class TreeView {
|
public class TreeView {
|
||||||
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
|
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
|
||||||
|
|
||||||
|
// the root of the tree
|
||||||
private final String rootPosition;
|
private final String rootPosition;
|
||||||
private final int maxLevels;
|
|
||||||
|
|
||||||
|
// how many levels / branches to display
|
||||||
|
private final int maxLevel;
|
||||||
|
|
||||||
|
// the actual tree object
|
||||||
private final ImmutableTreeNode view;
|
private final ImmutableTreeNode view;
|
||||||
|
|
||||||
public TreeView(PermissionVault source, String rootPosition, int maxLevels) {
|
public TreeView(PermissionVault source, String rootPosition, int maxLevel) {
|
||||||
this.rootPosition = rootPosition;
|
this.rootPosition = rootPosition;
|
||||||
this.maxLevels = maxLevels;
|
this.maxLevel = maxLevel;
|
||||||
|
|
||||||
Optional<TreeNode> root = findRoot(source);
|
Optional<TreeNode> root = findRoot(rootPosition, source);
|
||||||
this.view = root.map(TreeNode::makeImmutableCopy).orElse(null);
|
this.view = root.map(TreeNode::makeImmutableCopy).orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets if this TreeView has any content.
|
||||||
|
*
|
||||||
|
* @return true if the treeview has data
|
||||||
|
*/
|
||||||
public boolean hasData() {
|
public boolean hasData() {
|
||||||
return view != null;
|
return view != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the root of the tree node at the given position
|
||||||
|
*
|
||||||
|
* @param source the node source
|
||||||
|
* @return the root, if it exists
|
||||||
|
*/
|
||||||
|
private static Optional<TreeNode> findRoot(String rootPosition, PermissionVault source) {
|
||||||
|
// get the root of the permission vault
|
||||||
|
TreeNode root = source.getRootNode();
|
||||||
|
|
||||||
|
// just return the root
|
||||||
|
if (rootPosition.equals(".")) {
|
||||||
|
return Optional.of(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the parts of the node
|
||||||
|
List<String> parts = Splitter.on('.').omitEmptyStrings().splitToList(rootPosition);
|
||||||
|
|
||||||
|
// for each part
|
||||||
|
for (String part : parts) {
|
||||||
|
|
||||||
|
// check the current root has some children
|
||||||
|
if (!root.getChildren().isPresent()) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the current roots children
|
||||||
|
Map<String, TreeNode> branch = root.getChildren().get();
|
||||||
|
|
||||||
|
// get the new root
|
||||||
|
root = branch.get(part);
|
||||||
|
if (root == null) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Optional.of(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the view to a readable list
|
||||||
|
*
|
||||||
|
* <p>The list contains KV pairs, where the key is the tree padding/structure,
|
||||||
|
* and the value is the actual permission.</p>
|
||||||
|
*
|
||||||
|
* @return a list of the nodes in this view
|
||||||
|
*/
|
||||||
|
private List<Map.Entry<String, String>> asTreeList() {
|
||||||
|
// work out the prefix to apply
|
||||||
|
// since the view is relative, we need to prepend this to all permissions
|
||||||
|
String prefix = rootPosition.equals(".") ? "" : (rootPosition + ".");
|
||||||
|
|
||||||
|
|
||||||
|
List<Map.Entry<String, String>> ret = new ArrayList<>();
|
||||||
|
|
||||||
|
// iterate the node endings in the view
|
||||||
|
for (Map.Entry<Integer, String> s : view.getNodeEndings()) {
|
||||||
|
// don't include the node if it exceeds the max level
|
||||||
|
if (s.getKey() >= maxLevel) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate the tree padding characters from the node level
|
||||||
|
String treeStructure = Strings.repeat("│ ", s.getKey()) + "├── ";
|
||||||
|
// generate the permission, using the prefix and the node
|
||||||
|
String permission = prefix + s.getValue();
|
||||||
|
|
||||||
|
ret.add(Maps.immutableEntry(treeStructure, permission));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uploads the data contained in this TreeView to a paste, and returns the URL.
|
||||||
|
*
|
||||||
|
* @param version the plugin version string
|
||||||
|
* @return the url, or null
|
||||||
|
* @see PasteUtils#paste(String, List)
|
||||||
|
*/
|
||||||
public String uploadPasteData(String version) {
|
public String uploadPasteData(String version) {
|
||||||
|
// only paste if there is actually data here
|
||||||
if (!hasData()) {
|
if (!hasData()) {
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get the data contained in the view in a list form
|
||||||
|
// for each entry, the key is the padding tree characters
|
||||||
|
// and the value is the actual permission string
|
||||||
List<Map.Entry<String, String>> ret = asTreeList();
|
List<Map.Entry<String, String>> ret = asTreeList();
|
||||||
ImmutableList.Builder<String> builder = getPasteHeader(version, "none", ret.size());
|
|
||||||
builder.add("```");
|
|
||||||
|
|
||||||
|
// build the header of the paste
|
||||||
|
ImmutableList.Builder<String> builder = getPasteHeader(version, "none", ret.size());
|
||||||
|
|
||||||
|
// add the tree data
|
||||||
|
builder.add("```");
|
||||||
for (Map.Entry<String, String> e : ret) {
|
for (Map.Entry<String, String> e : ret) {
|
||||||
builder.add(e.getKey() + e.getValue());
|
builder.add(e.getKey() + e.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.add("```");
|
builder.add("```");
|
||||||
|
|
||||||
|
// clear the initial data map
|
||||||
ret.clear();
|
ret.clear();
|
||||||
|
|
||||||
|
// upload the return the data
|
||||||
return PasteUtils.paste("LuckPerms Permission Tree", ImmutableList.of(Maps.immutableEntry("luckperms-tree.md", builder.build().stream().collect(Collectors.joining("\n")))));
|
return PasteUtils.paste("LuckPerms Permission Tree", ImmutableList.of(Maps.immutableEntry("luckperms-tree.md", builder.build().stream().collect(Collectors.joining("\n")))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uploads the data contained in this TreeView to a paste, and returns the URL.
|
||||||
|
*
|
||||||
|
* <p>Unlike {@link #uploadPasteData(String)}, this method will check each permission
|
||||||
|
* against a corresponding user, and colorize the output depending on the check results.</p>
|
||||||
|
*
|
||||||
|
* @param version the plugin version string
|
||||||
|
* @param username the username of the reference user
|
||||||
|
* @param checker the permission data instance to check against
|
||||||
|
* @return the url, or null
|
||||||
|
* @see PasteUtils#paste(String, List)
|
||||||
|
*/
|
||||||
public String uploadPasteData(String version, String username, PermissionData checker) {
|
public String uploadPasteData(String version, String username, PermissionData checker) {
|
||||||
|
// only paste if there is actually data here
|
||||||
if (!hasData()) {
|
if (!hasData()) {
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get the data contained in the view in a list form
|
||||||
|
// for each entry, the key is the padding tree characters
|
||||||
|
// and the value is the actual permission string
|
||||||
List<Map.Entry<String, String>> ret = asTreeList();
|
List<Map.Entry<String, String>> ret = asTreeList();
|
||||||
ImmutableList.Builder<String> builder = getPasteHeader(version, username, ret.size());
|
|
||||||
builder.add("```diff");
|
|
||||||
|
|
||||||
|
// build the header of the paste
|
||||||
|
ImmutableList.Builder<String> builder = getPasteHeader(version, username, ret.size());
|
||||||
|
|
||||||
|
// add the tree data
|
||||||
|
builder.add("```diff");
|
||||||
for (Map.Entry<String, String> e : ret) {
|
for (Map.Entry<String, String> e : ret) {
|
||||||
|
|
||||||
|
// lookup a permission value for the node
|
||||||
Tristate tristate = checker.getPermissionValue(e.getValue());
|
Tristate tristate = checker.getPermissionValue(e.getValue());
|
||||||
|
|
||||||
|
// append the data to the paste
|
||||||
builder.add(getTristateDiffPrefix(tristate) + e.getKey() + e.getValue());
|
builder.add(getTristateDiffPrefix(tristate) + e.getKey() + e.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.add("```");
|
builder.add("```");
|
||||||
|
|
||||||
|
// clear the initial data map
|
||||||
ret.clear();
|
ret.clear();
|
||||||
|
|
||||||
|
// upload the return the data
|
||||||
return PasteUtils.paste("LuckPerms Permission Tree", ImmutableList.of(Maps.immutableEntry("luckperms-tree.md", builder.build().stream().collect(Collectors.joining("\n")))));
|
return PasteUtils.paste("LuckPerms Permission Tree", ImmutableList.of(Maps.immutableEntry("luckperms-tree.md", builder.build().stream().collect(Collectors.joining("\n")))));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,52 +249,9 @@ public class TreeView {
|
|||||||
.add("### Metadata")
|
.add("### Metadata")
|
||||||
.add("| Selection | Max Recursion | Reference User | Size | Produced at |")
|
.add("| Selection | Max Recursion | Reference User | Size | Produced at |")
|
||||||
.add("|-----------|---------------|----------------|------|-------------|")
|
.add("|-----------|---------------|----------------|------|-------------|")
|
||||||
.add("| " + selection + " | " + maxLevels + " | " + referenceUser + " | **" + size + "** | " + date + " |")
|
.add("| " + selection + " | " + maxLevel + " | " + referenceUser + " | **" + size + "** | " + date + " |")
|
||||||
.add("")
|
.add("")
|
||||||
.add("### Output");
|
.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,9 @@ package me.lucko.luckperms.common.treeview;
|
|||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.experimental.Accessors;
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a {@link TreeView}.
|
||||||
|
*/
|
||||||
@Accessors(fluent = true)
|
@Accessors(fluent = true)
|
||||||
public class TreeViewBuilder {
|
public class TreeViewBuilder {
|
||||||
public static TreeViewBuilder newBuilder() {
|
public static TreeViewBuilder newBuilder() {
|
||||||
|
@ -30,12 +30,26 @@ import lombok.Getter;
|
|||||||
|
|
||||||
import me.lucko.luckperms.api.Tristate;
|
import me.lucko.luckperms.api.Tristate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds the data from a permission check
|
||||||
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class CheckData {
|
public class CheckData {
|
||||||
|
|
||||||
private final String checked;
|
/**
|
||||||
private final String node;
|
* The name of the entity which was checked
|
||||||
private final Tristate value;
|
*/
|
||||||
|
private final String checkTarget;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The permission which was checked for
|
||||||
|
*/
|
||||||
|
private final String permission;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The result of the permission check
|
||||||
|
*/
|
||||||
|
private final Tristate result;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,141 @@
|
|||||||
|
package me.lucko.luckperms.common.verbose;
|
||||||
|
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
|
||||||
|
import me.lucko.luckperms.common.utils.Scripting;
|
||||||
|
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
|
import javax.script.ScriptEngine;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests verbose filters
|
||||||
|
*/
|
||||||
|
@UtilityClass
|
||||||
|
public class VerboseFilter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Evaluates whether the passed check data passes the filter
|
||||||
|
*
|
||||||
|
* @param data the check data
|
||||||
|
* @param filter the filter
|
||||||
|
* @return if the check data passes the filter
|
||||||
|
*/
|
||||||
|
public static boolean passesFilter(CheckData data, String filter) {
|
||||||
|
if (filter.equals("")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the script engine
|
||||||
|
ScriptEngine engine = Scripting.getScriptEngine();
|
||||||
|
if (engine == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// tokenize the filter
|
||||||
|
StringTokenizer tokenizer = new StringTokenizer(filter, " |&()!", true);
|
||||||
|
|
||||||
|
// build an expression which can be evaluated by the javascript engine
|
||||||
|
StringBuilder expressionBuilder = new StringBuilder();
|
||||||
|
|
||||||
|
// read the tokens
|
||||||
|
while (tokenizer.hasMoreTokens()) {
|
||||||
|
String token = tokenizer.nextToken();
|
||||||
|
|
||||||
|
// if the token is a delimiter, just append it to the expression
|
||||||
|
if (isDelim(token)) {
|
||||||
|
expressionBuilder.append(token);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// if the token is not a delimiter, it must be a string.
|
||||||
|
// we replace non-delimiters with a boolean depending on if the string matches the check data.
|
||||||
|
boolean value = data.getCheckTarget().equalsIgnoreCase(token) ||
|
||||||
|
data.getPermission().toLowerCase().startsWith(token.toLowerCase()) ||
|
||||||
|
data.getResult().name().equalsIgnoreCase(token);
|
||||||
|
|
||||||
|
expressionBuilder.append(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// build the expression
|
||||||
|
String expression = expressionBuilder.toString().replace("&", "&&").replace("|", "||");
|
||||||
|
|
||||||
|
// evaluate the expression using the script engine
|
||||||
|
try {
|
||||||
|
String result = engine.eval(expression).toString();
|
||||||
|
if (!result.equals("true") && !result.equals("false")) {
|
||||||
|
throw new IllegalArgumentException(expression + " - " + result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Boolean.parseBoolean(result);
|
||||||
|
|
||||||
|
} catch (Throwable t) {
|
||||||
|
t.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests whether a filter is valid
|
||||||
|
*
|
||||||
|
* @param filter the filter to test
|
||||||
|
* @return true if the filter is valid
|
||||||
|
*/
|
||||||
|
public static boolean isValidFilter(String filter) {
|
||||||
|
if (filter.equals("")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the script engine
|
||||||
|
ScriptEngine engine = Scripting.getScriptEngine();
|
||||||
|
if (engine == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// tokenize the filter
|
||||||
|
StringTokenizer tokenizer = new StringTokenizer(filter, " |&()!", true);
|
||||||
|
|
||||||
|
// build an expression which can be evaluated by the javascript engine
|
||||||
|
StringBuilder expressionBuilder = new StringBuilder();
|
||||||
|
|
||||||
|
// read the tokens
|
||||||
|
while (tokenizer.hasMoreTokens()) {
|
||||||
|
String token = tokenizer.nextToken();
|
||||||
|
|
||||||
|
// if the token is a delimiter, just append it to the expression
|
||||||
|
if (isDelim(token)) {
|
||||||
|
expressionBuilder.append(token);
|
||||||
|
} else {
|
||||||
|
expressionBuilder.append("true"); // dummy result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// build the expression
|
||||||
|
String expression = expressionBuilder.toString().replace("&", "&&").replace("|", "||");
|
||||||
|
|
||||||
|
// evaluate the expression using the script engine
|
||||||
|
try {
|
||||||
|
String result = engine.eval(expression).toString();
|
||||||
|
if (!result.equals("true") && !result.equals("false")) {
|
||||||
|
throw new IllegalArgumentException(expression + " - " + result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} catch (Throwable t) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isDelim(String token) {
|
||||||
|
return token.equals(" ") ||
|
||||||
|
token.equals("|") ||
|
||||||
|
token.equals("&") ||
|
||||||
|
token.equals("(") ||
|
||||||
|
token.equals(")") ||
|
||||||
|
token.equals("!");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -37,13 +37,22 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accepts {@link CheckData} and passes it onto registered {@link VerboseListener}s.
|
||||||
|
*/
|
||||||
public class VerboseHandler implements Runnable {
|
public class VerboseHandler implements Runnable {
|
||||||
private final String pluginVersion;
|
private final String pluginVersion;
|
||||||
|
|
||||||
|
// the listeners currently registered
|
||||||
private final Map<UUID, VerboseListener> listeners;
|
private final Map<UUID, VerboseListener> listeners;
|
||||||
|
|
||||||
|
// a queue of check data
|
||||||
private final Queue<CheckData> queue;
|
private final Queue<CheckData> queue;
|
||||||
|
|
||||||
|
// if there are any listeners currently registered
|
||||||
private boolean listening = false;
|
private boolean listening = false;
|
||||||
|
|
||||||
|
// if the handler should shutdown
|
||||||
@Setter
|
@Setter
|
||||||
private boolean shutdown = false;
|
private boolean shutdown = false;
|
||||||
|
|
||||||
@ -55,31 +64,67 @@ public class VerboseHandler implements Runnable {
|
|||||||
executor.execute(this);
|
executor.execute(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void offer(String checked, String node, Tristate value) {
|
/**
|
||||||
|
* Offers check data to the handler, to be eventually passed onto listeners.
|
||||||
|
*
|
||||||
|
* <p>The check data is added to a queue to be processed later, to avoid blocking
|
||||||
|
* the main thread each time a permission check is made.</p>
|
||||||
|
*
|
||||||
|
* @param checkTarget the target of the permission check
|
||||||
|
* @param permission the permission which was checked for
|
||||||
|
* @param result the result of the permission check
|
||||||
|
*/
|
||||||
|
public void offerCheckData(String checkTarget, String permission, Tristate result) {
|
||||||
|
// don't bother even processing the check if there are no listeners registered
|
||||||
if (!listening) {
|
if (!listening) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
queue.offer(new CheckData(checked, node, value));
|
// add the check data to a queue to be processed later.
|
||||||
|
queue.offer(new CheckData(checkTarget, permission, result));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void register(Sender sender, String filter, boolean notify) {
|
/**
|
||||||
|
* Registers a new listener for the given player.
|
||||||
|
*
|
||||||
|
* @param sender the sender to notify, if notify is true
|
||||||
|
* @param filter the filter string
|
||||||
|
* @param notify if the sender should be notified in chat on each check
|
||||||
|
*/
|
||||||
|
public void registerListener(Sender sender, String filter, boolean notify) {
|
||||||
listening = true;
|
listening = true;
|
||||||
listeners.put(sender.getUuid(), new VerboseListener(pluginVersion, sender, filter, notify));
|
listeners.put(sender.getUuid(), new VerboseListener(pluginVersion, sender, filter, notify));
|
||||||
}
|
}
|
||||||
|
|
||||||
public VerboseListener unregister(UUID uuid) {
|
/**
|
||||||
|
* Removes a listener for a given player
|
||||||
|
*
|
||||||
|
* @param uuid the players uuid
|
||||||
|
* @return the existing listener, if one was actually registered
|
||||||
|
*/
|
||||||
|
public VerboseListener unregisterListener(UUID uuid) {
|
||||||
|
// immediately flush, so the listener gets all current data
|
||||||
flush();
|
flush();
|
||||||
|
|
||||||
VerboseListener ret = listeners.remove(uuid);
|
VerboseListener ret = listeners.remove(uuid);
|
||||||
|
|
||||||
|
// stop listening if there are no listeners left
|
||||||
if (listeners.isEmpty()) {
|
if (listeners.isEmpty()) {
|
||||||
listening = false;
|
listening = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
while (true) {
|
while (true) {
|
||||||
|
// remove listeners where the sender is no longer valid
|
||||||
|
listeners.values().removeIf(l -> !l.getNotifiedSender().isValid());
|
||||||
|
if (listeners.isEmpty()) {
|
||||||
|
listening = false;
|
||||||
|
}
|
||||||
|
|
||||||
flush();
|
flush();
|
||||||
|
|
||||||
if (shutdown) {
|
if (shutdown) {
|
||||||
@ -92,6 +137,9 @@ public class VerboseHandler implements Runnable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flushes the current check data to the listeners.
|
||||||
|
*/
|
||||||
public synchronized void flush() {
|
public synchronized void flush() {
|
||||||
for (CheckData e; (e = queue.poll()) != null; ) {
|
for (CheckData e; (e = queue.poll()) != null; ) {
|
||||||
for (VerboseListener listener : listeners.values()) {
|
for (VerboseListener listener : listeners.values()) {
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
package me.lucko.luckperms.common.verbose;
|
package me.lucko.luckperms.common.verbose;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
@ -35,49 +36,59 @@ import me.lucko.luckperms.common.commands.sender.Sender;
|
|||||||
import me.lucko.luckperms.common.locale.Message;
|
import me.lucko.luckperms.common.locale.Message;
|
||||||
import me.lucko.luckperms.common.utils.DateUtil;
|
import me.lucko.luckperms.common.utils.DateUtil;
|
||||||
import me.lucko.luckperms.common.utils.PasteUtils;
|
import me.lucko.luckperms.common.utils.PasteUtils;
|
||||||
import me.lucko.luckperms.common.utils.Scripting;
|
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.StringTokenizer;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import javax.script.ScriptEngine;
|
/**
|
||||||
|
* Accepts and processes {@link CheckData}, passed from the {@link VerboseHandler}.
|
||||||
|
*/
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class VerboseListener {
|
public class VerboseListener {
|
||||||
private static final int DATA_TRUNCATION = 10000;
|
|
||||||
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
|
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
|
||||||
private static final Function<Tristate, String> TRISTATE_COLOR = tristate -> {
|
|
||||||
switch (tristate) {
|
|
||||||
case TRUE:
|
|
||||||
return "&2";
|
|
||||||
case FALSE:
|
|
||||||
return "&c";
|
|
||||||
default:
|
|
||||||
return "&7";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
// how much data should we store before stopping.
|
||||||
|
private static final int DATA_TRUNCATION = 10000;
|
||||||
|
|
||||||
|
// the time when the listener was first registered
|
||||||
private final long startTime = System.currentTimeMillis();
|
private final long startTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
// the version of the plugin. (used when we paste data to gist)
|
||||||
private final String pluginVersion;
|
private final String pluginVersion;
|
||||||
private final Sender holder;
|
|
||||||
|
// the sender to notify each time the listener processes a check which passes the filter
|
||||||
|
@Getter
|
||||||
|
private final Sender notifiedSender;
|
||||||
|
|
||||||
|
// the filter string
|
||||||
private final String filter;
|
private final String filter;
|
||||||
|
|
||||||
|
// if we should notify the sender
|
||||||
private final boolean notify;
|
private final boolean notify;
|
||||||
|
|
||||||
|
// the number of checks we have processed
|
||||||
private final AtomicInteger counter = new AtomicInteger(0);
|
private final AtomicInteger counter = new AtomicInteger(0);
|
||||||
private final AtomicInteger matchedCounter = new AtomicInteger(0);
|
|
||||||
private final List<CheckData> results = new ArrayList<>();
|
|
||||||
|
|
||||||
|
// the number of checks we have processed and accepted, based on the filter rules for this
|
||||||
|
// listener
|
||||||
|
private final AtomicInteger matchedCounter = new AtomicInteger(0);
|
||||||
|
|
||||||
|
// the checks which passed the filter, up to a max size of #DATA_TRUNCATION
|
||||||
|
private final List<CheckData> results = new ArrayList<>(DATA_TRUNCATION / 10);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accepts and processes check data.
|
||||||
|
*
|
||||||
|
* @param data the data to process
|
||||||
|
*/
|
||||||
public void acceptData(CheckData data) {
|
public void acceptData(CheckData data) {
|
||||||
counter.incrementAndGet();
|
counter.incrementAndGet();
|
||||||
if (!matches(data, filter)) {
|
if (!VerboseFilter.passesFilter(data, filter)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
matchedCounter.incrementAndGet();
|
matchedCounter.incrementAndGet();
|
||||||
@ -87,98 +98,23 @@ public class VerboseListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (notify) {
|
if (notify) {
|
||||||
Message.VERBOSE_LOG.send(holder, "&a" + data.getChecked() + "&7 -- &a" + data.getNode() + "&7 -- " + TRISTATE_COLOR.apply(data.getValue()) + data.getValue().name().toLowerCase() + "");
|
Message.VERBOSE_LOG.send(notifiedSender, "&a" + data.getCheckTarget() + "&7 -- &a" + data.getPermission() + "&7 -- " + getTristateColor(data.getResult()) + data.getResult().name().toLowerCase() + "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean matches(CheckData data, String filter) {
|
/**
|
||||||
if (filter.equals("")) {
|
* Uploads the captured data in this listener to a paste and returns the url
|
||||||
return true;
|
*
|
||||||
}
|
* @return the url
|
||||||
|
* @see PasteUtils#paste(String, List)
|
||||||
ScriptEngine engine = Scripting.getScriptEngine();
|
*/
|
||||||
if (engine == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
StringTokenizer tokenizer = new StringTokenizer(filter, " |&()!", true);
|
|
||||||
StringBuilder expression = new StringBuilder();
|
|
||||||
|
|
||||||
while (tokenizer.hasMoreTokens()) {
|
|
||||||
String token = tokenizer.nextToken();
|
|
||||||
if (!isDelim(token)) {
|
|
||||||
boolean b = data.getChecked().equalsIgnoreCase(token) ||
|
|
||||||
data.getNode().toLowerCase().startsWith(token.toLowerCase()) ||
|
|
||||||
data.getValue().name().equalsIgnoreCase(token);
|
|
||||||
|
|
||||||
token = "" + b;
|
|
||||||
}
|
|
||||||
|
|
||||||
expression.append(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
String exp = expression.toString().replace("&", "&&").replace("|", "||");
|
|
||||||
String result = engine.eval(exp).toString();
|
|
||||||
if (!result.equals("true") && !result.equals("false")) {
|
|
||||||
throw new IllegalArgumentException(exp + " - " + result);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Boolean.parseBoolean(result);
|
|
||||||
|
|
||||||
} catch (Throwable t) {
|
|
||||||
t.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isValidFilter(String filter) {
|
|
||||||
if (filter.equals("")) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ScriptEngine engine = Scripting.getScriptEngine();
|
|
||||||
if (engine == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
StringTokenizer tokenizer = new StringTokenizer(filter, " |&()!", true);
|
|
||||||
StringBuilder expression = new StringBuilder();
|
|
||||||
|
|
||||||
while (tokenizer.hasMoreTokens()) {
|
|
||||||
String token = tokenizer.nextToken();
|
|
||||||
if (!isDelim(token)) {
|
|
||||||
token = "true"; // dummy result
|
|
||||||
}
|
|
||||||
|
|
||||||
expression.append(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
String exp = expression.toString().replace("&", "&&").replace("|", "||");
|
|
||||||
String result = engine.eval(exp).toString();
|
|
||||||
if (!result.equals("true") && !result.equals("false")) {
|
|
||||||
throw new IllegalArgumentException(exp + " - " + result);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
} catch (Throwable t) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isDelim(String token) {
|
|
||||||
return token.equals(" ") || token.equals("|") || token.equals("&") || token.equals("(") || token.equals(")") || token.equals("!");
|
|
||||||
}
|
|
||||||
|
|
||||||
public String uploadPasteData() {
|
public String uploadPasteData() {
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
String startDate = DATE_FORMAT.format(new Date(startTime));
|
String startDate = DATE_FORMAT.format(new Date(startTime));
|
||||||
String endDate = DATE_FORMAT.format(new Date(now));
|
String endDate = DATE_FORMAT.format(new Date(now));
|
||||||
long secondsTaken = (now - startTime) / 1000L;
|
long secondsTaken = (now - startTime) / 1000L;
|
||||||
String duration = DateUtil.formatTime(secondsTaken);
|
String duration = DateUtil.formatTime(secondsTaken);
|
||||||
|
|
||||||
String filter = this.filter;
|
String filter = this.filter;
|
||||||
if (filter == null || filter.equals("")){
|
if (filter == null || filter.equals("")){
|
||||||
filter = "any";
|
filter = "any";
|
||||||
@ -186,7 +122,7 @@ public class VerboseListener {
|
|||||||
filter = "`" + filter + "`";
|
filter = "`" + filter + "`";
|
||||||
}
|
}
|
||||||
|
|
||||||
ImmutableList.Builder<String> output = ImmutableList.<String>builder()
|
ImmutableList.Builder<String> prettyOutput = ImmutableList.<String>builder()
|
||||||
.add("## Verbose Checking Output")
|
.add("## Verbose Checking Output")
|
||||||
.add("#### This file was automatically generated by [LuckPerms](https://github.com/lucko/LuckPerms) " + pluginVersion)
|
.add("#### This file was automatically generated by [LuckPerms](https://github.com/lucko/LuckPerms) " + pluginVersion)
|
||||||
.add("")
|
.add("")
|
||||||
@ -197,33 +133,33 @@ public class VerboseListener {
|
|||||||
.add("| End Time | " + endDate + " |")
|
.add("| End Time | " + endDate + " |")
|
||||||
.add("| Duration | " + duration +" |")
|
.add("| Duration | " + duration +" |")
|
||||||
.add("| Count | **" + matchedCounter.get() + "** / " + counter + " |")
|
.add("| Count | **" + matchedCounter.get() + "** / " + counter + " |")
|
||||||
.add("| User | " + holder.getName() + " |")
|
.add("| User | " + notifiedSender.getName() + " |")
|
||||||
.add("| Filter | " + filter + " |")
|
.add("| Filter | " + filter + " |")
|
||||||
.add("");
|
.add("");
|
||||||
|
|
||||||
if (matchedCounter.get() > results.size()) {
|
if (matchedCounter.get() > results.size()) {
|
||||||
output.add("**WARN:** Result set exceeded max size of " + DATA_TRUNCATION + ". The output below was truncated to " + DATA_TRUNCATION + " entries.");
|
prettyOutput.add("**WARN:** Result set exceeded max size of " + DATA_TRUNCATION + ". The output below was truncated to " + DATA_TRUNCATION + " entries.");
|
||||||
output.add("");
|
prettyOutput.add("");
|
||||||
}
|
}
|
||||||
|
|
||||||
output.add("### Output")
|
prettyOutput.add("### Output")
|
||||||
.add("Format: `<checked>` `<permission>` `<value>`")
|
.add("Format: `<checked>` `<permission>` `<value>`")
|
||||||
.add("")
|
.add("")
|
||||||
.add("___")
|
.add("___")
|
||||||
.add("");
|
.add("");
|
||||||
|
|
||||||
ImmutableList.Builder<String> data = ImmutableList.<String>builder()
|
ImmutableList.Builder<String> csvOutput = ImmutableList.<String>builder()
|
||||||
.add("User,Permission,Result");
|
.add("User,Permission,Result");
|
||||||
|
|
||||||
results.stream()
|
results.forEach(c -> {
|
||||||
.peek(c -> output.add("`" + c.getChecked() + "` - " + c.getNode() + " - **" + c.getValue().toString() + "** "))
|
prettyOutput.add("`" + c.getCheckTarget() + "` - " + c.getPermission() + " - **" + c.getResult().toString() + "** ");
|
||||||
.forEach(c -> data.add(escapeCommas(c.getChecked()) + "," + escapeCommas(c.getNode()) + "," + c.getValue().name().toLowerCase()));
|
csvOutput.add(escapeCommas(c.getCheckTarget()) + "," + escapeCommas(c.getPermission()) + "," + c.getResult().name().toLowerCase());
|
||||||
|
});
|
||||||
results.clear();
|
results.clear();
|
||||||
|
|
||||||
List<Map.Entry<String, String>> content = ImmutableList.of(
|
List<Map.Entry<String, String>> content = ImmutableList.of(
|
||||||
Maps.immutableEntry("luckperms-verbose.md", output.build().stream().collect(Collectors.joining("\n"))),
|
Maps.immutableEntry("luckperms-verbose.md", prettyOutput.build().stream().collect(Collectors.joining("\n"))),
|
||||||
Maps.immutableEntry("raw-data.csv", data.build().stream().collect(Collectors.joining("\n")))
|
Maps.immutableEntry("raw-data.csv", csvOutput.build().stream().collect(Collectors.joining("\n")))
|
||||||
);
|
);
|
||||||
|
|
||||||
return PasteUtils.paste("LuckPerms Verbose Checking Output", content);
|
return PasteUtils.paste("LuckPerms Verbose Checking Output", content);
|
||||||
@ -233,4 +169,15 @@ public class VerboseListener {
|
|||||||
return s.contains(",") ? "\"" + s + "\"" : s;
|
return s.contains(",") ? "\"" + s + "\"" : s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String getTristateColor(Tristate tristate) {
|
||||||
|
switch (tristate) {
|
||||||
|
case TRUE:
|
||||||
|
return "&2";
|
||||||
|
case FALSE:
|
||||||
|
return "&c";
|
||||||
|
default:
|
||||||
|
return "&7";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,7 @@ import me.lucko.luckperms.common.config.LuckPermsConfiguration;
|
|||||||
import me.lucko.luckperms.common.constants.CommandPermission;
|
import me.lucko.luckperms.common.constants.CommandPermission;
|
||||||
import me.lucko.luckperms.common.contexts.ContextManager;
|
import me.lucko.luckperms.common.contexts.ContextManager;
|
||||||
import me.lucko.luckperms.common.contexts.LuckPermsCalculator;
|
import me.lucko.luckperms.common.contexts.LuckPermsCalculator;
|
||||||
|
import me.lucko.luckperms.common.data.ImporterSender;
|
||||||
import me.lucko.luckperms.common.dependencies.DependencyManager;
|
import me.lucko.luckperms.common.dependencies.DependencyManager;
|
||||||
import me.lucko.luckperms.common.locale.LocaleManager;
|
import me.lucko.luckperms.common.locale.LocaleManager;
|
||||||
import me.lucko.luckperms.common.locale.NoopLocaleManager;
|
import me.lucko.luckperms.common.locale.NoopLocaleManager;
|
||||||
@ -66,7 +67,6 @@ import me.lucko.luckperms.common.tasks.CacheHousekeepingTask;
|
|||||||
import me.lucko.luckperms.common.tasks.ExpireTemporaryTask;
|
import me.lucko.luckperms.common.tasks.ExpireTemporaryTask;
|
||||||
import me.lucko.luckperms.common.tasks.UpdateTask;
|
import me.lucko.luckperms.common.tasks.UpdateTask;
|
||||||
import me.lucko.luckperms.common.treeview.PermissionVault;
|
import me.lucko.luckperms.common.treeview.PermissionVault;
|
||||||
import me.lucko.luckperms.common.utils.FakeSender;
|
|
||||||
import me.lucko.luckperms.common.utils.UuidCache;
|
import me.lucko.luckperms.common.utils.UuidCache;
|
||||||
import me.lucko.luckperms.common.verbose.VerboseHandler;
|
import me.lucko.luckperms.common.verbose.VerboseHandler;
|
||||||
import me.lucko.luckperms.sponge.calculators.SpongeCalculatorFactory;
|
import me.lucko.luckperms.sponge.calculators.SpongeCalculatorFactory;
|
||||||
@ -497,7 +497,7 @@ public class LPSpongePlugin implements LuckPermsPlugin {
|
|||||||
@Override
|
@Override
|
||||||
public Sender getConsoleSender() {
|
public Sender getConsoleSender() {
|
||||||
if (!game.isServerAvailable()) {
|
if (!game.isServerAvailable()) {
|
||||||
return new FakeSender(this, s -> logger.info(s));
|
return new ImporterSender(this, s -> logger.info(s));
|
||||||
}
|
}
|
||||||
return getSenderFactory().wrap(game.getServer().getConsole());
|
return getSenderFactory().wrap(game.getServer().getConsole());
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ import java.util.Optional;
|
|||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@EqualsAndHashCode(of = {"id", "description", "owner"})
|
@EqualsAndHashCode(of = "id")
|
||||||
@ToString(of = {"id", "description", "owner"})
|
@ToString(of = {"id", "description", "owner"})
|
||||||
public final class LuckPermsPermissionDescription implements LPPermissionDescription {
|
public final class LuckPermsPermissionDescription implements LPPermissionDescription {
|
||||||
|
|
||||||
|
@ -73,6 +73,7 @@ import java.io.File;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@ -202,7 +203,19 @@ public class LuckPermsService implements LPPermissionService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ImmutableSet<LPPermissionDescription> getDescriptions() {
|
public ImmutableSet<LPPermissionDescription> getDescriptions() {
|
||||||
return ImmutableSet.copyOf(descriptionSet);
|
Set<LPPermissionDescription> descriptions = new HashSet<>(descriptionSet);
|
||||||
|
|
||||||
|
// collect known values from the permission vault
|
||||||
|
for (String knownPermission : plugin.getPermissionVault().getKnownPermissions()) {
|
||||||
|
LPPermissionDescription desc = new LuckPermsPermissionDescription(this, knownPermission, null, null);
|
||||||
|
|
||||||
|
// don't override plugin defined values
|
||||||
|
if (!descriptions.contains(desc)) {
|
||||||
|
descriptions.add(desc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ImmutableSet.copyOf(descriptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -247,7 +247,7 @@ public class PersistedSubject implements LPSubject {
|
|||||||
public Tristate getPermissionValue(@NonNull ImmutableContextSet contexts, @NonNull String node) {
|
public Tristate getPermissionValue(@NonNull ImmutableContextSet contexts, @NonNull String node) {
|
||||||
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.INTERNAL_SUBJECT_GET_PERMISSION_VALUE)) {
|
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.INTERNAL_SUBJECT_GET_PERMISSION_VALUE)) {
|
||||||
Tristate t = permissionLookupCache.get(PermissionLookup.of(node, contexts));
|
Tristate t = permissionLookupCache.get(PermissionLookup.of(node, contexts));
|
||||||
service.getPlugin().getVerboseHandler().offer("local:" + getParentCollection().getIdentifier() + "/" + identifier, node, t);
|
service.getPlugin().getVerboseHandler().offerCheckData("local:" + getParentCollection().getIdentifier() + "/" + identifier, node, t);
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user