diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/VaultChatHook.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/VaultChatHook.java index 0becab53d..5e580466b 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/VaultChatHook.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/VaultChatHook.java @@ -302,7 +302,7 @@ public class VaultChatHook extends Chat { world = perms.correctWorld(world); Contexts contexts; - if (holder instanceof User) { + if (holder.getType().isUser()) { contexts = perms.createContextForWorldLookup(perms.getPlugin().getPlayer((User) holder), world); } else { contexts = perms.createContextForWorldLookup(world); @@ -322,7 +322,7 @@ public class VaultChatHook extends Chat { world = perms.correctWorld(world); Contexts contexts; - if (holder instanceof User) { + if (holder.getType().isUser()) { contexts = perms.createContextForWorldLookup(perms.getPlugin().getPlayer((User) holder), world); } else { contexts = perms.createContextForWorldLookup(world); diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/VaultPermissionHook.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/VaultPermissionHook.java index 69a743d28..04e844844 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/VaultPermissionHook.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/VaultPermissionHook.java @@ -444,11 +444,11 @@ public class VaultPermissionHook extends Permission { } void holderSave(PermissionHolder holder) { - if (holder instanceof User) { + if (holder.getType().isUser()) { User u = (User) holder; plugin.getStorage().saveUser(u).thenRunAsync(() -> u.getRefreshBuffer().request(), plugin.getScheduler().async()); } - if (holder instanceof Group) { + if (holder.getType().isGroup()) { Group g = (Group) holder; plugin.getStorage().saveGroup(g).thenRunAsync(() -> plugin.getUpdateTaskBuffer().request(), plugin.getScheduler().async()); } diff --git a/common/src/main/java/me/lucko/luckperms/common/actionlog/ExtendedLogEntry.java b/common/src/main/java/me/lucko/luckperms/common/actionlog/ExtendedLogEntry.java index 3fa5bebe7..49ec46420 100644 --- a/common/src/main/java/me/lucko/luckperms/common/actionlog/ExtendedLogEntry.java +++ b/common/src/main/java/me/lucko/luckperms/common/actionlog/ExtendedLogEntry.java @@ -283,11 +283,11 @@ public class ExtendedLogEntry implements LogEntry { } public ExtendedLogEntryBuilder acted(PermissionHolder acted) { - if (acted instanceof User) { + if (acted.getType().isUser()) { actedName(((User) acted).getName().orElse("null")); acted(((User) acted).getUuid()); type(Type.USER); - } else if (acted instanceof Group) { + } else if (acted.getType().isGroup()) { actedName(((Group) acted).getName()); type(Type.GROUP); } diff --git a/common/src/main/java/me/lucko/luckperms/common/api/delegates/model/ApiPermissionHolder.java b/common/src/main/java/me/lucko/luckperms/common/api/delegates/model/ApiPermissionHolder.java index f15483433..9bd5781d4 100644 --- a/common/src/main/java/me/lucko/luckperms/common/api/delegates/model/ApiPermissionHolder.java +++ b/common/src/main/java/me/lucko/luckperms/common/api/delegates/model/ApiPermissionHolder.java @@ -66,7 +66,7 @@ public class ApiPermissionHolder implements PermissionHolder { @Override public String getFriendlyName() { - if (handle instanceof Group) { + if (handle.getType().isGroup()) { Group group = (Group) this.handle; return group.getDisplayName().orElse(group.getName()); } @@ -181,7 +181,7 @@ public class ApiPermissionHolder implements PermissionHolder { @Override public void clearMatching(Predicate test) { handle.removeIf(test); - if (handle instanceof User) { + if (handle.getType().isUser()) { handle.getPlugin().getUserManager().giveDefaultIfNeeded((User) handle, false); } } diff --git a/common/src/main/java/me/lucko/luckperms/common/backup/Exporter.java b/common/src/main/java/me/lucko/luckperms/common/backup/Exporter.java index b76056e97..fb2d8b94a 100644 --- a/common/src/main/java/me/lucko/luckperms/common/backup/Exporter.java +++ b/common/src/main/java/me/lucko/luckperms/common/backup/Exporter.java @@ -35,6 +35,7 @@ import me.lucko.luckperms.common.model.Track; import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.node.NodeFactory; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; +import me.lucko.luckperms.common.references.HolderType; import me.lucko.luckperms.common.storage.Storage; import me.lucko.luckperms.common.utils.Cycle; @@ -124,7 +125,7 @@ public class Exporter implements Runnable { write(writer, "# Export group: " + group.getName()); for (Node node : group.getEnduringNodes().values()) { - write(writer, "/lp " + NodeFactory.nodeAsCommand(node, group.getName(), true, true)); + write(writer, "/lp " + NodeFactory.nodeAsCommand(node, group.getName(), HolderType.GROUP, true)); } write(writer, ""); log.logAllProgress("Exported {} groups so far.", groupCount.incrementAndGet()); @@ -230,7 +231,7 @@ public class Exporter implements Runnable { continue; } - output.add("/lp " + NodeFactory.nodeAsCommand(node, user.getUuid().toString(), false, true)); + output.add("/lp " + NodeFactory.nodeAsCommand(node, user.getUuid().toString(), HolderType.USER, true)); } if (!user.getPrimaryGroup().getStoredValue().orElse("default").equalsIgnoreCase("default")) { diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/abstraction/SharedSubCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/abstraction/SharedSubCommand.java index 7a165669e..fb295fe47 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/abstraction/SharedSubCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/abstraction/SharedSubCommand.java @@ -118,13 +118,13 @@ public abstract class SharedSubCommand { } public static void save(PermissionHolder holder, Sender sender, LuckPermsPlugin plugin) { - if (holder instanceof User) { + if (holder.getType().isUser()) { User user = ((User) holder); SubCommand.save(user, sender, plugin); return; } - if (holder instanceof Group) { + if (holder.getType().isGroup()) { Group group = ((Group) holder); SubCommand.save(group, sender, plugin); return; diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/meta/MetaInfo.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/meta/MetaInfo.java index 1513dcede..605c93fd3 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/meta/MetaInfo.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/meta/MetaInfo.java @@ -43,7 +43,6 @@ import me.lucko.luckperms.common.locale.LocaleManager; import me.lucko.luckperms.common.locale.Message; import me.lucko.luckperms.common.model.Group; import me.lucko.luckperms.common.model.PermissionHolder; -import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.node.NodeFactory; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.utils.Predicates; @@ -168,12 +167,12 @@ public class MetaInfo extends SharedSubCommand { "¥7Click to remove this " + type.name().toLowerCase() + " from " + holder.getFriendlyName() ), '¥')); - boolean group = !(holder instanceof User); - String command = "/" + label + " " + NodeFactory.nodeAsCommand(node, group ? holder.getObjectName() : holder.getFriendlyName(), group, false); + String command = "/" + label + " " + NodeFactory.nodeAsCommand(node, holder.getType().isGroup() ? holder.getObjectName() : holder.getFriendlyName(), holder.getType(), false); + ClickEvent clickEvent = new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, command); return component -> { component.hoverEvent(hoverEvent); - component.clickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, command)); + component.clickEvent(clickEvent); }; } @@ -192,12 +191,12 @@ public class MetaInfo extends SharedSubCommand { "¥7Click to remove this meta pair from " + holder.getFriendlyName() ), '¥')); - boolean group = !(holder instanceof User); - String command = "/" + label + " " + NodeFactory.nodeAsCommand(node, group ? holder.getObjectName() : holder.getFriendlyName(), group, false); + String command = "/" + label + " " + NodeFactory.nodeAsCommand(node, holder.getType().isGroup() ? holder.getObjectName() : holder.getFriendlyName(), holder.getType(), false); + ClickEvent clickEvent = new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, command); return component -> { component.hoverEvent(hoverEvent); - component.clickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, command)); + component.clickEvent(clickEvent); }; } } diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/other/HolderEditor.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/other/HolderEditor.java index b9a9038be..559e70bc7 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/other/HolderEditor.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/other/HolderEditor.java @@ -71,7 +71,7 @@ public class HolderEditor extends SubCommand { JsonObject payload = new JsonObject(); payload.addProperty("who", WebEditorUtils.getHolderIdentifier(holder)); payload.addProperty("whoFriendly", holder.getFriendlyName()); - if (holder instanceof User) { + if (holder.getType().isUser()) { payload.addProperty("whoUuid", ((User) holder).getUuid().toString()); } payload.addProperty("cmdAlias", label); diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/other/HolderShowTracks.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/other/HolderShowTracks.java index a61d36fcc..5c911bee9 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/other/HolderShowTracks.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/other/HolderShowTracks.java @@ -25,6 +25,8 @@ package me.lucko.luckperms.common.commands.impl.generic.other; +import com.google.common.collect.Maps; + import me.lucko.luckperms.api.Node; import me.lucko.luckperms.common.commands.ArgumentPermissions; import me.lucko.luckperms.common.commands.CommandException; @@ -37,10 +39,13 @@ import me.lucko.luckperms.common.locale.CommandSpec; import me.lucko.luckperms.common.locale.LocaleManager; import me.lucko.luckperms.common.locale.Message; import me.lucko.luckperms.common.model.PermissionHolder; +import me.lucko.luckperms.common.model.Track; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.utils.Predicates; +import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @@ -63,32 +68,32 @@ public class HolderShowTracks extends SubCommand Set nodes = holder.getEnduringNodes().values().stream() .filter(Node::isGroupNode) + .filter(Node::getValuePrimitive) .filter(Node::isPermanent) .collect(Collectors.toSet()); - StringBuilder sb = new StringBuilder(); + List> lines = new ArrayList<>(); for (Node node : nodes) { String name = node.getGroupName(); - - plugin.getTrackManager().getAll().values().stream() + List tracks = plugin.getTrackManager().getAll().values().stream() .filter(t -> t.containsGroup(name)) - .forEach(t -> sb.append("&a") - .append(t.getName()) - .append(": ") - .append(CommandUtils.listToArrowSep(t.getGroups(), name)) - .append(CommandUtils.getAppendableNodeContextString(node)) - .append("\n") - ); + .collect(Collectors.toList()); + + for (Track t : tracks) { + lines.add(Maps.immutableEntry(t, CommandUtils.listToArrowSep(t.getGroups(), name) + CommandUtils.getAppendableNodeContextString(node))); + } } - if (sb.length() == 0) { + if (lines.isEmpty()) { Message.LIST_TRACKS_EMPTY.send(sender, holder.getFriendlyName()); return CommandResult.SUCCESS; - } else { - sb.deleteCharAt(sb.length() - 1); - Message.LIST_TRACKS.send(sender, holder.getFriendlyName(), sb.toString()); - return CommandResult.SUCCESS; } + + Message.LIST_TRACKS.send(sender, holder.getFriendlyName()); + for (Map.Entry line : lines) { + Message.LIST_TRACKS_ENTRY.send(sender, line.getKey().getName(), line.getValue()); + } + return CommandResult.SUCCESS; } } diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/parent/ParentClearTrack.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/parent/ParentClearTrack.java index 9d892acb2..da406ef6e 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/parent/ParentClearTrack.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/parent/ParentClearTrack.java @@ -103,7 +103,7 @@ public class ParentClearTrack extends SharedSubCommand { holder.removeIf(node -> node.isGroupNode() && node.getFullContexts().equals(context) && track.containsGroup(node.getGroupName())); } - if (holder instanceof User) { + if (holder.getType().isUser()) { plugin.getUserManager().giveDefaultIfNeeded(((User) holder), false); } diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/parent/ParentInfo.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/parent/ParentInfo.java index f2deba3cb..1924bda18 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/parent/ParentInfo.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/parent/ParentInfo.java @@ -32,35 +32,38 @@ import me.lucko.luckperms.common.commands.CommandException; import me.lucko.luckperms.common.commands.CommandResult; import me.lucko.luckperms.common.commands.abstraction.SharedSubCommand; import me.lucko.luckperms.common.commands.sender.Sender; +import me.lucko.luckperms.common.commands.utils.ArgumentUtils; import me.lucko.luckperms.common.commands.utils.CommandUtils; +import me.lucko.luckperms.common.commands.utils.SortMode; +import me.lucko.luckperms.common.commands.utils.SortType; import me.lucko.luckperms.common.constants.CommandPermission; import me.lucko.luckperms.common.constants.Constants; import me.lucko.luckperms.common.locale.CommandSpec; import me.lucko.luckperms.common.locale.LocaleManager; import me.lucko.luckperms.common.locale.Message; import me.lucko.luckperms.common.model.PermissionHolder; -import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.node.NodeFactory; +import me.lucko.luckperms.common.node.NodeWithContextComparator; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; +import me.lucko.luckperms.common.utils.CollationKeyCache; import me.lucko.luckperms.common.utils.DateUtil; import me.lucko.luckperms.common.utils.Predicates; import me.lucko.luckperms.common.utils.TextUtils; import net.kyori.text.BuildableComponent; -import net.kyori.text.Component; import net.kyori.text.TextComponent; import net.kyori.text.event.ClickEvent; import net.kyori.text.event.HoverEvent; -import net.kyori.text.format.TextColor; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.List; -import java.util.SortedSet; import java.util.function.Consumer; public class ParentInfo extends SharedSubCommand { public ParentInfo(LocaleManager locale) { - super(CommandSpec.PARENT_INFO.spec(locale), "info", CommandPermission.USER_PARENT_INFO, CommandPermission.GROUP_PARENT_INFO, Predicates.alwaysFalse()); + super(CommandSpec.PARENT_INFO.spec(locale), "info", CommandPermission.USER_PARENT_INFO, CommandPermission.GROUP_PARENT_INFO, Predicates.notInRange(0, 2)); } @Override @@ -70,58 +73,67 @@ public class ParentInfo extends SharedSubCommand { return CommandResult.NO_PERMISSION; } - Component ent = permGroupsToMessage(holder.getOwnNodesSorted(), holder, label); - Message.LISTPARENTS.send(sender, holder.getFriendlyName()); - sender.sendMessage(ent); + int page = ArgumentUtils.handleIntOrElse(0, args, 1); + SortMode sortMode = SortMode.determine(args); - Component tempEnt = tempGroupsToMessage(holder.getOwnNodesSorted(), holder, label); - Message.LISTPARENTS_TEMP.send(sender, holder.getFriendlyName()); - sender.sendMessage(tempEnt); + // get the holders nodes + List nodes = new ArrayList<>(holder.getOwnNodesSorted()); + + // remove irrelevant types (these are displayed in the other info commands) + nodes.removeIf(node -> !node.isGroupNode() || !node.getValuePrimitive()); + + // handle empty + if (nodes.isEmpty()) { + Message.PARENT_INFO_NO_DATA.send(sender, holder.getFriendlyName()); + return CommandResult.SUCCESS; + } + + // sort the list alphabetically instead + if (sortMode.getType() == SortType.ALPHABETICALLY) { + nodes.sort(ALPHABETICAL_NODE_COMPARATOR); + } + + // reverse the order if necessary + if (!sortMode.isAscending()) { + Collections.reverse(nodes); + } + + int pageIndex = page - 1; + List> pages = CommandUtils.divideList(nodes, 19); + + if (pageIndex < 0 || pageIndex >= pages.size()) { + page = 1; + pageIndex = 0; + } + + List content = pages.get(pageIndex); + + // send header + Message.PARENT_INFO.send(sender, holder.getFriendlyName(), page, pages.size(), nodes.size()); + + // send content + for (LocalizedNode node : content) { + String s = "&3> &a" + node.getGroupName() + CommandUtils.getAppendableNodeContextString(node); + if (node.isTemporary()) { + s += "\n&2 expires in " + DateUtil.formatDateDiff(node.getExpiryUnixTime()); + } + + TextComponent message = TextUtils.fromLegacy(s, Constants.FORMAT_CHAR).toBuilder().applyDeep(makeFancy(holder, label, node)).build(); + sender.sendMessage(message); + } return CommandResult.SUCCESS; } - private static Component permGroupsToMessage(SortedSet nodes, PermissionHolder holder, String label) { - List page = new ArrayList<>(); - for (Node node : nodes) { - if (!node.isGroupNode()) continue; - if (!node.getValuePrimitive()) continue; - if (node.isTemporary()) continue; - page.add(node); + private static final Comparator ALPHABETICAL_NODE_COMPARATOR = (o1, o2) -> { + int i = CollationKeyCache.compareStrings(o1.getGroupName(), o2.getGroupName()); + if (i != 0) { + return i; } - if (page.isEmpty()) { - return TextComponent.builder("None").color(TextColor.DARK_AQUA).build(); - } - - TextComponent.Builder message = TextComponent.builder(""); - for (Node node : page) { - String s = "&3> &a" + node.getGroupName() + CommandUtils.getAppendableNodeContextString(node) + "\n"; - message.append(TextUtils.fromLegacy(s, Constants.FORMAT_CHAR).toBuilder().applyDeep(makeFancy(holder, label, node)).build()); - } - return message.build(); - } - - private static Component tempGroupsToMessage(SortedSet nodes, PermissionHolder holder, String label) { - List page = new ArrayList<>(); - for (Node node : nodes) { - if (!node.isGroupNode()) continue; - if (!node.getValuePrimitive()) continue; - if (node.isPermanent()) continue; - page.add(node); - } - - if (page.isEmpty()) { - return TextComponent.builder("None").color(TextColor.DARK_AQUA).build(); - } - - TextComponent.Builder message = TextComponent.builder(""); - for (Node node : page) { - String s = "&3> &a" + node.getGroupName() + CommandUtils.getAppendableNodeContextString(node) + "\n&2- expires in " + DateUtil.formatDateDiff(node.getExpiryUnixTime()) + "\n"; - message.append(TextUtils.fromLegacy(s, Constants.FORMAT_CHAR).toBuilder().applyDeep(makeFancy(holder, label, node)).build()); - } - return message.build(); - } + // fallback to priority + return NodeWithContextComparator.reverse().compare(o1, o2); + }; private static Consumer> makeFancy(PermissionHolder holder, String label, Node node) { HoverEvent hoverEvent = new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextUtils.fromLegacy(TextUtils.joinNewline( @@ -130,12 +142,12 @@ public class ParentInfo extends SharedSubCommand { "&7Click to remove this parent from " + holder.getFriendlyName() ), Constants.FORMAT_CHAR)); - boolean group = !(holder instanceof User); - String command = "/" + label + " " + NodeFactory.nodeAsCommand(node, group ? holder.getObjectName() : holder.getFriendlyName(), group, false); + String command = "/" + label + " " + NodeFactory.nodeAsCommand(node, holder.getType().isGroup() ? holder.getObjectName() : holder.getFriendlyName(), holder.getType(), false); + ClickEvent clickEvent = new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, command); return component -> { component.hoverEvent(hoverEvent); - component.clickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, command)); + component.clickEvent(clickEvent); }; } } diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/parent/ParentRemove.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/parent/ParentRemove.java index 5c555ef64..544fc297a 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/parent/ParentRemove.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/parent/ParentRemove.java @@ -75,7 +75,7 @@ public class ParentRemove extends SharedSubCommand { return CommandResult.NO_PERMISSION; } - if (holder instanceof User) { + if (holder.getType().isUser()) { User user = (User) holder; boolean shouldPrevent = plugin.getConfiguration().get(ConfigKeys.PREVENT_PRIMARY_GROUP_REMOVAL) && @@ -97,7 +97,7 @@ public class ParentRemove extends SharedSubCommand { .action("parent", "remove", groupName, context) .build().submit(plugin, sender); - if (holder instanceof User) { + if (holder.getType().isUser()) { plugin.getUserManager().giveDefaultIfNeeded(((User) holder), false); } diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/parent/ParentSet.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/parent/ParentSet.java index d6159b791..df7a8b561 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/parent/ParentSet.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/parent/ParentSet.java @@ -86,7 +86,7 @@ public class ParentSet extends SharedSubCommand { holder.clearParents(context, false); holder.setInheritGroup(group, context); - if (holder instanceof User) { + if (holder.getType().isUser()) { ((User) holder).getPrimaryGroup().setStoredValue(group.getName()); } diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/permission/PermissionInfo.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/permission/PermissionInfo.java index 779c906e7..7d813021a 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/permission/PermissionInfo.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/permission/PermissionInfo.java @@ -25,8 +25,6 @@ package me.lucko.luckperms.common.commands.impl.generic.permission; -import com.google.common.collect.Maps; - import me.lucko.luckperms.api.LocalizedNode; import me.lucko.luckperms.api.Node; import me.lucko.luckperms.common.commands.ArgumentPermissions; @@ -36,30 +34,31 @@ import me.lucko.luckperms.common.commands.abstraction.SharedSubCommand; import me.lucko.luckperms.common.commands.sender.Sender; import me.lucko.luckperms.common.commands.utils.ArgumentUtils; import me.lucko.luckperms.common.commands.utils.CommandUtils; +import me.lucko.luckperms.common.commands.utils.SortMode; +import me.lucko.luckperms.common.commands.utils.SortType; import me.lucko.luckperms.common.constants.CommandPermission; import me.lucko.luckperms.common.constants.Constants; import me.lucko.luckperms.common.locale.CommandSpec; import me.lucko.luckperms.common.locale.LocaleManager; import me.lucko.luckperms.common.locale.Message; import me.lucko.luckperms.common.model.PermissionHolder; -import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.node.NodeFactory; +import me.lucko.luckperms.common.node.NodeWithContextComparator; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; +import me.lucko.luckperms.common.utils.CollationKeyCache; import me.lucko.luckperms.common.utils.DateUtil; import me.lucko.luckperms.common.utils.Predicates; import me.lucko.luckperms.common.utils.TextUtils; import net.kyori.text.BuildableComponent; -import net.kyori.text.Component; import net.kyori.text.TextComponent; import net.kyori.text.event.ClickEvent; import net.kyori.text.event.HoverEvent; -import net.kyori.text.format.TextColor; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.List; -import java.util.Map; -import java.util.SortedSet; import java.util.function.Consumer; public class PermissionInfo extends SharedSubCommand { @@ -74,111 +73,72 @@ public class PermissionInfo extends SharedSubCommand { return CommandResult.NO_PERMISSION; } - String filter = null; - if (args.size() == 1) { - // it might be a filter, if it's a number, then it relates to a page. - try { - Integer.parseInt(args.get(0)); - } catch (NumberFormatException e) { - // it's not a number, so assume it's the filter. - filter = args.get(0); - } - } else if (args.size() == 2) { - filter = args.get(1); - } - int page = ArgumentUtils.handleIntOrElse(0, args, 1); + SortMode sortMode = SortMode.determine(args); - Map.Entry ent = nodesToMessage(false, filter, holder.getOwnNodesSorted(), holder, label, page, sender.isConsole()); - if (ent.getValue() != null) { - Message.LISTNODES_WITH_PAGE.send(sender, holder.getFriendlyName(), ent.getValue()); - sender.sendMessage(ent.getKey()); - } else { - Message.LISTNODES.send(sender, holder.getFriendlyName()); - sender.sendMessage(ent.getKey()); + // get the holders nodes + List nodes = new ArrayList<>(holder.getOwnNodesSorted()); + + // remove irrelevant types (these are displayed in the other info commands) + nodes.removeIf(node -> + // remove if the node is a group node, and if the value isn't false and if the group actually exists + (node.isGroupNode() && node.getValuePrimitive() && plugin.getGroupManager().isLoaded(node.getGroupName())) || + // remove if the node is a meta node + node.isPrefix() || node.isSuffix() || node.isMeta() + ); + + // handle empty + if (nodes.isEmpty()) { + Message.PERMISSION_INFO_NO_DATA.send(sender, holder.getFriendlyName()); + return CommandResult.SUCCESS; } - Map.Entry tempEnt = nodesToMessage(true, filter, holder.getOwnNodesSorted(), holder, label, page, sender.isConsole()); - if (tempEnt.getValue() != null) { - Message.LISTNODES_TEMP_WITH_PAGE.send(sender, holder.getFriendlyName(), tempEnt.getValue()); - sender.sendMessage(tempEnt.getKey()); - } else { - Message.LISTNODES_TEMP.send(sender, holder.getFriendlyName()); - sender.sendMessage(tempEnt.getKey()); + // sort the list alphabetically instead + if (sortMode.getType() == SortType.ALPHABETICALLY) { + nodes.sort(ALPHABETICAL_NODE_COMPARATOR); + } + + // reverse the order if necessary + if (!sortMode.isAscending()) { + Collections.reverse(nodes); + } + + int pageIndex = page - 1; + List> pages = CommandUtils.divideList(nodes, 19); + + if (pageIndex < 0 || pageIndex >= pages.size()) { + page = 1; + pageIndex = 0; + } + + List content = pages.get(pageIndex); + + // send header + Message.PERMISSION_INFO.send(sender, holder.getFriendlyName(), page, pages.size(), nodes.size()); + + // send content + for (LocalizedNode node : content) { + String s = "&3> " + (node.getValuePrimitive() ? "&a" : "&c") + node.getPermission() + (sender.isConsole() ? " &7(" + node.getValuePrimitive() + "&7)" : "") + CommandUtils.getAppendableNodeContextString(node); + if (node.isTemporary()) { + s += "\n&2- expires in " + DateUtil.formatDateDiff(node.getExpiryUnixTime()); + } + + TextComponent message = TextUtils.fromLegacy(s, Constants.FORMAT_CHAR).toBuilder().applyDeep(makeFancy(holder, label, node)).build(); + sender.sendMessage(message); } return CommandResult.SUCCESS; } - private static Map.Entry nodesToMessage(boolean temp, String filter, SortedSet nodes, PermissionHolder holder, String label, int pageNumber, boolean console) { - // parse the filter - String nodeFilter = null; - Map.Entry contextFilter = null; - - if (filter != null) { - int index = filter.indexOf('='); - - context: - if (index != -1) { - String key = filter.substring(0, index); - if (key.equals("")) break context; - - String value = filter.substring(index + 1); - if (value.equals("")) break context; - - contextFilter = Maps.immutableEntry(key, value); - } - - if (contextFilter == null) { - nodeFilter = filter; - } + private static final Comparator ALPHABETICAL_NODE_COMPARATOR = (o1, o2) -> { + int i = CollationKeyCache.compareStrings(o1.getPermission(), o2.getPermission()); + if (i != 0) { + return i; } - List l = new ArrayList<>(); - for (Node node : nodes) { - if ((node.isGroupNode() && node.getValuePrimitive()) || node.isPrefix() || node.isSuffix() || node.isMeta()) continue; - - // check against filters - if (nodeFilter != null && !node.getPermission().startsWith(nodeFilter)) continue; - if (contextFilter != null && !node.getFullContexts().hasIgnoreCase(contextFilter.getKey(), contextFilter.getValue())) continue; - if (temp != node.isTemporary()) continue; - - l.add(node); - } - - if (l.isEmpty()) { - return Maps.immutableEntry(TextComponent.builder("None").color(TextColor.DARK_AQUA).build(), null); - } - - int index = pageNumber - 1; - List> pages = CommandUtils.divideList(l, 15); - - if (index < 0 || index >= pages.size()) { - pageNumber = 1; - index = 0; - } - - List page = pages.get(index); - - TextComponent.Builder message = TextComponent.builder(""); - String title = "&7(showing page &f" + pageNumber + "&7 of &f" + pages.size() + "&7 - &f" + l.size() + "&7 entries"; - if (filter != null) { - title += " - filtered by &f\"" + filter + "\"&7)"; - } else { - title += ")"; - } - - for (Node node : page) { - String s = "&3> " + (node.getValuePrimitive() ? "&a" : "&c") + node.getPermission() + (console ? " &7(" + node.getValuePrimitive() + "&7)" : "") + CommandUtils.getAppendableNodeContextString(node) + "\n"; - if (temp) { - s += "&2- expires in " + DateUtil.formatDateDiff(node.getExpiryUnixTime()) + "\n"; - } - - message.append(TextUtils.fromLegacy(s, Constants.FORMAT_CHAR).toBuilder().applyDeep(makeFancy(holder, label, node)).build()); - } - - return Maps.immutableEntry(message.build(), title); - } + // fallback to priority + return NodeWithContextComparator.reverse().compare(o1, o2); + }; private static Consumer> makeFancy(PermissionHolder holder, String label, Node node) { HoverEvent hoverEvent = new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextUtils.fromLegacy(TextUtils.joinNewline( @@ -187,12 +147,12 @@ public class PermissionInfo extends SharedSubCommand { "¥7Click to remove this node from " + holder.getFriendlyName() ), '¥')); - boolean group = !(holder instanceof User); - String command = "/" + label + " " + NodeFactory.nodeAsCommand(node, group ? holder.getObjectName() : holder.getFriendlyName(), group, false); + String command = "/" + label + " " + NodeFactory.nodeAsCommand(node, holder.getType().isGroup() ? holder.getObjectName() : holder.getFriendlyName(), holder.getType(), false); + ClickEvent clickEvent = new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, command); return component -> { component.hoverEvent(hoverEvent); - component.clickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, command)); + component.clickEvent(clickEvent); }; } } diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/group/GroupListMembers.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/group/GroupListMembers.java index 96b16cd3a..91968490a 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/group/GroupListMembers.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/group/GroupListMembers.java @@ -25,6 +25,8 @@ package me.lucko.luckperms.common.commands.impl.group; +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.LoadingCache; import com.google.common.collect.Maps; import me.lucko.luckperms.api.HeldPermission; @@ -44,20 +46,17 @@ import me.lucko.luckperms.common.locale.Message; import me.lucko.luckperms.common.model.Group; import me.lucko.luckperms.common.node.NodeFactory; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; +import me.lucko.luckperms.common.references.HolderType; import me.lucko.luckperms.common.utils.DateUtil; import me.lucko.luckperms.common.utils.Predicates; import me.lucko.luckperms.common.utils.TextUtils; import net.kyori.text.BuildableComponent; -import net.kyori.text.Component; import net.kyori.text.TextComponent; import net.kyori.text.event.ClickEvent; import net.kyori.text.event.HoverEvent; -import net.kyori.text.format.TextColor; import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; @@ -90,96 +89,60 @@ public class GroupListMembers extends SubCommand { Message.SEARCH_RESULT.send(sender, users + groups, users, groups); - Map uuidLookups = new HashMap<>(); - Function lookupFunc = uuid -> uuidLookups.computeIfAbsent(uuid, u -> { - String s = plugin.getStorage().getName(u).join(); - if (s == null || s.isEmpty() || s.equals("null")) { - s = u.toString(); - } - return s; - }); - - Map.Entry msgUsers = searchUserResultToMessage(matchedUsers, lookupFunc, label, page); - Map.Entry msgGroups = searchGroupResultToMessage(matchedGroups, label, page); - - if (msgUsers.getValue() != null) { - Message.SEARCH_SHOWING_USERS_WITH_PAGE.send(sender, msgUsers.getValue()); - sender.sendMessage(msgUsers.getKey()); - } else { - Message.SEARCH_SHOWING_USERS.send(sender); - sender.sendMessage(msgUsers.getKey()); + if (!matchedUsers.isEmpty()) { + LoadingCache uuidLookups = Caffeine.newBuilder() + .build(u -> { + String s = plugin.getStorage().getName(u).join(); + if (s == null || s.isEmpty() || s.equals("null")) { + s = u.toString(); + } + return s; + }); + sendResult(sender, matchedUsers, uuidLookups::get, Message.SEARCH_SHOWING_USERS, HolderType.USER, label, page); } - if (msgGroups.getValue() != null) { - Message.SEARCH_SHOWING_GROUPS_WITH_PAGE.send(sender, msgGroups.getValue()); - sender.sendMessage(msgGroups.getKey()); - } else { - Message.SEARCH_SHOWING_GROUPS.send(sender); - sender.sendMessage(msgGroups.getKey()); + if (!matchedGroups.isEmpty()) { + sendResult(sender, matchedGroups, Function.identity(), Message.SEARCH_SHOWING_GROUPS, HolderType.GROUP, label, page); } return CommandResult.SUCCESS; } - private static Map.Entry searchUserResultToMessage(List> results, Function uuidLookup, String label, int pageNumber) { - if (results.isEmpty()) { - return Maps.immutableEntry(TextComponent.builder("None").color(TextColor.DARK_AQUA).build(), null); + private static void sendResult(Sender sender, List> results, Function lookupFunction, Message headerMessage, HolderType holderType, String label, int page) { + results = new ArrayList<>(results); + + // we need a deterministic sort order + // even though we're comparing uuids here in some cases - it doesn't matter + // the import thing is that the order is the same each time the command is executed + results.sort((o1, o2) -> { + Comparable h1 = (Comparable) o1.getHolder(); + Comparable h2 = (Comparable) o2.getHolder(); + //noinspection unchecked + return h1.compareTo(h2); + }); + + int pageIndex = page - 1; + List>> pages = CommandUtils.divideList(results, 15); + + if (pageIndex < 0 || pageIndex >= pages.size()) { + page = 1; + pageIndex = 0; } - List> sorted = new ArrayList<>(results); - sorted.sort(Comparator.comparing(HeldPermission::getHolder)); + List> content = pages.get(pageIndex); - int index = pageNumber - 1; - List>> pages = CommandUtils.divideList(sorted, 15); - - if (index < 0 || index >= pages.size()) { - pageNumber = 1; - index = 0; - } - - List> page = pages.get(index); - List>> uuidMappedPage = page.stream() - .map(hp -> Maps.immutableEntry(uuidLookup.apply(hp.getHolder()), hp)) + List>> mappedContent = content.stream() + .map(hp -> Maps.immutableEntry(lookupFunction.apply(hp.getHolder()), hp)) .collect(Collectors.toList()); - TextComponent.Builder message = TextComponent.builder(""); - String title = "&7(page &f" + pageNumber + "&7 of &f" + pages.size() + "&7 - &f" + sorted.size() + "&7 entries)"; + // send header + headerMessage.send(sender, page, pages.size(), results.size()); - for (Map.Entry> ent : uuidMappedPage) { - String s = "&3> &b" + ent.getKey() + " " + getNodeExpiryString(ent.getValue().asNode()) + CommandUtils.getAppendableNodeContextString(ent.getValue().asNode()) + "\n"; - message.append(TextUtils.fromLegacy(s, Constants.FORMAT_CHAR).toBuilder().applyDeep(makeFancy(ent.getKey(), false, label, ent.getValue())).build()); + for (Map.Entry> ent : mappedContent) { + String s = "&3> &b" + ent.getKey() + " " + getNodeExpiryString(ent.getValue().asNode()) + CommandUtils.getAppendableNodeContextString(ent.getValue().asNode()); + TextComponent message = TextUtils.fromLegacy(s, Constants.FORMAT_CHAR).toBuilder().applyDeep(makeFancy(ent.getKey(), holderType, label, ent.getValue())).build(); + sender.sendMessage(message); } - - return Maps.immutableEntry(message.build(), title); - } - - private static Map.Entry searchGroupResultToMessage(List> results, String label, int pageNumber) { - if (results.isEmpty()) { - return Maps.immutableEntry(TextComponent.builder("None").color(TextColor.DARK_AQUA).build(), null); - } - - List> sorted = new ArrayList<>(results); - sorted.sort(Comparator.comparing(HeldPermission::getHolder)); - - int index = pageNumber - 1; - List>> pages = CommandUtils.divideList(sorted, 15); - - if (index < 0 || index >= pages.size()) { - pageNumber = 1; - index = 0; - } - - List> page = pages.get(index); - - TextComponent.Builder message = TextComponent.builder(""); - String title = "&7(page &f" + pageNumber + "&7 of &f" + pages.size() + "&7 - &f" + sorted.size() + "&7 entries)"; - - for (HeldPermission ent : page) { - String s = "&3> &b" + ent.getHolder() + " " + getNodeExpiryString(ent.asNode()) + CommandUtils.getAppendableNodeContextString(ent.asNode()) + "\n"; - message.append(TextUtils.fromLegacy(s, Constants.FORMAT_CHAR).toBuilder().applyDeep(makeFancy(ent.getHolder(), true, label, ent)).build()); - } - - return Maps.immutableEntry(message.build(), title); } private static String getNodeExpiryString(Node node) { @@ -190,18 +153,19 @@ public class GroupListMembers extends SubCommand { return " &8(&7expires in " + DateUtil.formatDateDiff(node.getExpiryUnixTime()) + "&8)"; } - private static Consumer> makeFancy(String holderName, boolean group, String label, HeldPermission perm) { + private static Consumer> makeFancy(String holderName, HolderType holderType, String label, HeldPermission perm) { HoverEvent hoverEvent = new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextUtils.fromLegacy(TextUtils.joinNewline( - "&3> " + (perm.asNode().getValuePrimitive() ? "&a" : "&c") + perm.asNode().getGroupName(), + "&3> &b" + perm.asNode().getGroupName(), " ", "&7Click to remove this parent from " + holderName ), Constants.FORMAT_CHAR)); - String command = "/" + label + " " + NodeFactory.nodeAsCommand(perm.asNode(), holderName, group, false); + String command = "/" + label + " " + NodeFactory.nodeAsCommand(perm.asNode(), holderName, holderType, false); + ClickEvent clickEvent = new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, command); return component -> { component.hoverEvent(hoverEvent); - component.clickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, command)); + component.clickEvent(clickEvent); }; } } diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/SearchCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/SearchCommand.java index 9cc712d5c..b659037d5 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/SearchCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/SearchCommand.java @@ -25,6 +25,8 @@ package me.lucko.luckperms.common.commands.impl.misc; +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.LoadingCache; import com.google.common.collect.Maps; import me.lucko.luckperms.api.HeldPermission; @@ -43,20 +45,17 @@ import me.lucko.luckperms.common.locale.LocaleManager; import me.lucko.luckperms.common.locale.Message; import me.lucko.luckperms.common.node.NodeFactory; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; +import me.lucko.luckperms.common.references.HolderType; import me.lucko.luckperms.common.utils.DateUtil; import me.lucko.luckperms.common.utils.Predicates; import me.lucko.luckperms.common.utils.TextUtils; import net.kyori.text.BuildableComponent; -import net.kyori.text.Component; import net.kyori.text.TextComponent; import net.kyori.text.event.ClickEvent; import net.kyori.text.event.HoverEvent; -import net.kyori.text.format.TextColor; import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; @@ -84,32 +83,20 @@ public class SearchCommand extends SingleCommand { Message.SEARCH_RESULT.send(sender, users + groups, users, groups); - Map uuidLookups = new HashMap<>(); - Function lookupFunc = uuid -> uuidLookups.computeIfAbsent(uuid, u -> { - String s = plugin.getStorage().getName(u).join(); - if (s == null || s.isEmpty() || s.equals("null")) { - s = u.toString(); - } - return s; - }); - - Map.Entry msgUsers = searchUserResultToMessage(matchedUsers, lookupFunc, label, page); - Map.Entry msgGroups = searchGroupResultToMessage(matchedGroups, label, page); - - if (msgUsers.getValue() != null) { - Message.SEARCH_SHOWING_USERS_WITH_PAGE.send(sender, msgUsers.getValue()); - sender.sendMessage(msgUsers.getKey()); - } else { - Message.SEARCH_SHOWING_USERS.send(sender); - sender.sendMessage(msgUsers.getKey()); + if (!matchedUsers.isEmpty()) { + LoadingCache uuidLookups = Caffeine.newBuilder() + .build(u -> { + String s = plugin.getStorage().getName(u).join(); + if (s == null || s.isEmpty() || s.equals("null")) { + s = u.toString(); + } + return s; + }); + sendResult(sender, matchedUsers, uuidLookups::get, Message.SEARCH_SHOWING_USERS, HolderType.USER, label, page); } - if (msgGroups.getValue() != null) { - Message.SEARCH_SHOWING_GROUPS_WITH_PAGE.send(sender, msgGroups.getValue()); - sender.sendMessage(msgGroups.getKey()); - } else { - Message.SEARCH_SHOWING_GROUPS.send(sender); - sender.sendMessage(msgGroups.getKey()); + if (!matchedGroups.isEmpty()) { + sendResult(sender, matchedGroups, Function.identity(), Message.SEARCH_SHOWING_GROUPS, HolderType.GROUP, label, page); } return CommandResult.SUCCESS; @@ -120,65 +107,41 @@ public class SearchCommand extends SingleCommand { return SubCommand.getPermissionTabComplete(args, plugin.getPermissionVault()); } - private static Map.Entry searchUserResultToMessage(List> results, Function uuidLookup, String label, int pageNumber) { - if (results.isEmpty()) { - return Maps.immutableEntry(TextComponent.builder("None").color(TextColor.DARK_AQUA).build(), null); + private static void sendResult(Sender sender, List> results, Function lookupFunction, Message headerMessage, HolderType holderType, String label, int page) { + results = new ArrayList<>(results); + + // we need a deterministic sort order + // even though we're comparing uuids here in some cases - it doesn't matter + // the import thing is that the order is the same each time the command is executed + results.sort((o1, o2) -> { + Comparable h1 = (Comparable) o1.getHolder(); + Comparable h2 = (Comparable) o2.getHolder(); + //noinspection unchecked + return h1.compareTo(h2); + }); + + int pageIndex = page - 1; + List>> pages = CommandUtils.divideList(results, 15); + + if (pageIndex < 0 || pageIndex >= pages.size()) { + page = 1; + pageIndex = 0; } - List> sorted = new ArrayList<>(results); - sorted.sort(Comparator.comparing(HeldPermission::getHolder)); + List> content = pages.get(pageIndex); - int index = pageNumber - 1; - List>> pages = CommandUtils.divideList(sorted, 15); - - if (index < 0 || index >= pages.size()) { - pageNumber = 1; - index = 0; - } - - List> page = pages.get(index); - List>> uuidMappedPage = page.stream() - .map(hp -> Maps.immutableEntry(uuidLookup.apply(hp.getHolder()), hp)) + List>> mappedContent = content.stream() + .map(hp -> Maps.immutableEntry(lookupFunction.apply(hp.getHolder()), hp)) .collect(Collectors.toList()); - TextComponent.Builder message = TextComponent.builder(""); - String title = "&7(page &f" + pageNumber + "&7 of &f" + pages.size() + "&7 - &f" + sorted.size() + "&7 entries)"; + // send header + headerMessage.send(sender, page, pages.size(), results.size()); - for (Map.Entry> ent : uuidMappedPage) { - String s = "&3> &b" + ent.getKey() + " &7- " + (ent.getValue().getValue() ? "&a" : "&c") + ent.getValue().getValue() + getNodeExpiryString(ent.getValue().asNode()) + CommandUtils.getAppendableNodeContextString(ent.getValue().asNode()) + "\n"; - message.append(TextUtils.fromLegacy(s, Constants.FORMAT_CHAR).toBuilder().applyDeep(makeFancy(ent.getKey(), false, label, ent.getValue())).build()); + for (Map.Entry> ent : mappedContent) { + String s = "&3> &b" + ent.getKey() + " &7- " + (ent.getValue().getValue() ? "&a" : "&c") + ent.getValue().getValue() + getNodeExpiryString(ent.getValue().asNode()) + CommandUtils.getAppendableNodeContextString(ent.getValue().asNode()); + TextComponent message = TextUtils.fromLegacy(s, Constants.FORMAT_CHAR).toBuilder().applyDeep(makeFancy(ent.getKey(), holderType, label, ent.getValue())).build(); + sender.sendMessage(message); } - - return Maps.immutableEntry(message.build(), title); - } - - private static Map.Entry searchGroupResultToMessage(List> results, String label, int pageNumber) { - if (results.isEmpty()) { - return Maps.immutableEntry(TextComponent.builder("None").color(TextColor.DARK_AQUA).build(), null); - } - - List> sorted = new ArrayList<>(results); - sorted.sort(Comparator.comparing(HeldPermission::getHolder)); - - int index = pageNumber - 1; - List>> pages = CommandUtils.divideList(sorted, 15); - - if (index < 0 || index >= pages.size()) { - pageNumber = 1; - index = 0; - } - - List> page = pages.get(index); - - TextComponent.Builder message = TextComponent.builder(""); - String title = "&7(page &f" + pageNumber + "&7 of &f" + pages.size() + "&7 - &f" + sorted.size() + "&7 entries)"; - - for (HeldPermission ent : page) { - String s = "&3> &b" + ent.getHolder() + " &7- " + (ent.getValue() ? "&a" : "&c") + ent.getValue() + getNodeExpiryString(ent.asNode()) + CommandUtils.getAppendableNodeContextString(ent.asNode()) + "\n"; - message.append(TextUtils.fromLegacy(s, Constants.FORMAT_CHAR).toBuilder().applyDeep(makeFancy(ent.getHolder(), true, label, ent)).build()); - } - - return Maps.immutableEntry(message.build(), title); } private static String getNodeExpiryString(Node node) { @@ -189,18 +152,19 @@ public class SearchCommand extends SingleCommand { return " &8(&7expires in " + DateUtil.formatDateDiff(node.getExpiryUnixTime()) + "&8)"; } - private static Consumer> makeFancy(String holderName, boolean group, String label, HeldPermission perm) { + private static Consumer> makeFancy(String holderName, HolderType holderType, String label, HeldPermission perm) { HoverEvent hoverEvent = new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextUtils.fromLegacy(TextUtils.joinNewline( "&3> " + (perm.asNode().getValuePrimitive() ? "&a" : "&c") + perm.asNode().getPermission(), " ", "&7Click to remove this node from " + holderName ), Constants.FORMAT_CHAR)); - String command = "/" + label + " " + NodeFactory.nodeAsCommand(perm.asNode(), holderName, group, false); + String command = "/" + label + " " + NodeFactory.nodeAsCommand(perm.asNode(), holderName, holderType, false); + ClickEvent clickEvent = new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, command); return component -> { component.hoverEvent(hoverEvent); - component.clickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, command)); + component.clickEvent(clickEvent); }; } } diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/utils/SortMode.java b/common/src/main/java/me/lucko/luckperms/common/commands/utils/SortMode.java new file mode 100644 index 000000000..cf976d785 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/commands/utils/SortMode.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2017 Lucko (Luck) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.common.commands.utils; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.List; + +@Getter +@AllArgsConstructor +public class SortMode { + + public static SortMode determine(List args) { + SortType type = SortType.PRIORITY; + boolean ascending = true; + for (String arg : args) { + if (arg.equals("!") || arg.equalsIgnoreCase("reverse") || arg.equalsIgnoreCase("reversed")) { + ascending = false; + } else if (arg.equalsIgnoreCase("priority")) { + type = SortType.PRIORITY; + ascending = true; + } else if (arg.equalsIgnoreCase("!priority")) { + type = SortType.PRIORITY; + ascending = false; + } else if (arg.equalsIgnoreCase("alphabetically") || arg.equalsIgnoreCase("abc")) { + type = SortType.ALPHABETICALLY; + ascending = true; + } else if (arg.equalsIgnoreCase("!alphabetically") || arg.equalsIgnoreCase("!abc")) { + type = SortType.ALPHABETICALLY; + ascending = false; + } else { + continue; + } + break; + } + return new SortMode(type, ascending); + } + + private final SortType type; + private final boolean ascending; +} diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/utils/SortType.java b/common/src/main/java/me/lucko/luckperms/common/commands/utils/SortType.java new file mode 100644 index 000000000..68557e5c4 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/commands/utils/SortType.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2017 Lucko (Luck) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.common.commands.utils; + +public enum SortType { + + PRIORITY, + ALPHABETICALLY; + + @Override + public String toString() { + return name().toLowerCase(); + } +} diff --git a/common/src/main/java/me/lucko/luckperms/common/locale/CommandSpec.java b/common/src/main/java/me/lucko/luckperms/common/locale/CommandSpec.java index a4e488bd8..e54998506 100644 --- a/common/src/main/java/me/lucko/luckperms/common/locale/CommandSpec.java +++ b/common/src/main/java/me/lucko/luckperms/common/locale/CommandSpec.java @@ -191,7 +191,7 @@ public enum CommandSpec { PERMISSION_INFO("Lists the permission nodes the object has", Arg.list( Arg.create("page", false, "the page to view"), - Arg.create("filter", false, "the string to filter by") + Arg.create("sort mode", false, "how to sort the entries") ) ), PERMISSION_SET("Sets a permission for the object", @@ -234,7 +234,12 @@ public enum CommandSpec { ) ), - PARENT_INFO("Lists the groups that this object inherits from"), + PARENT_INFO("Lists the groups that this object inherits from", + Arg.list( + Arg.create("page", false, "the page to view"), + Arg.create("sort mode", false, "how to sort the entries") + ) + ), PARENT_SET("Removes all other groups the object inherits already and adds them to the one given", Arg.list( Arg.create("group", true, "the group to set to"), diff --git a/common/src/main/java/me/lucko/luckperms/common/locale/Message.java b/common/src/main/java/me/lucko/luckperms/common/locale/Message.java index b1a20f93a..e99b564ac 100644 --- a/common/src/main/java/me/lucko/luckperms/common/locale/Message.java +++ b/common/src/main/java/me/lucko/luckperms/common/locale/Message.java @@ -113,10 +113,9 @@ public enum Message { SEARCH_SEARCHING("&aSearching for users and groups with &b{}&a...", true), SEARCH_SEARCHING_MEMBERS("&aSearching for users and groups who inherit from &b{}&a...", true), SEARCH_RESULT("&aFound &b{}&a entries from &b{}&a users and &b{}&a groups.", true), - SEARCH_SHOWING_USERS("&bShowing user entries:", true), - SEARCH_SHOWING_GROUPS("&bShowing group entries:", true), - SEARCH_SHOWING_USERS_WITH_PAGE("&bShowing user entries: {}", true), - SEARCH_SHOWING_GROUPS_WITH_PAGE("&bShowing group entries: {}", true), + + SEARCH_SHOWING_USERS("&bShowing user entries: &7(showing page &f{}&7 of &f{}&7 - &f{}&7 entries)", true), + SEARCH_SHOWING_GROUPS("&bShowing group entries: &7(showing page &f{}&7 of &f{}&7 - &f{}&7 entries)", true), APPLY_EDITS_INVALID_CODE("&cInvalid code. &7({})", true), APPLY_EDITS_UNABLE_TO_READ("&cUnable to read data using the given code. &7({})", true), @@ -198,14 +197,14 @@ public enum Message { TRACKS_LIST("&aTracks: {}", true), - LISTNODES("&b{}'s Permissions:", true), - LISTNODES_WITH_PAGE("&b{}'s Permissions: {}", true), - LISTNODES_TEMP("&b{}'s Temporary Permissions:", true), - LISTNODES_TEMP_WITH_PAGE("&b{}'s Temporary Permissions: {}", true), + PERMISSION_INFO("&b{}'s Permissions: &7(showing page &f{}&7 of &f{}&7 - &f{}&7 entries)", true), + PERMISSION_INFO_NO_DATA("&b{}&a does not have any permissions set.", true), - LISTPARENTS("&b{}'s Parent Groups:", true), - LISTPARENTS_TEMP("&b{}'s Temporary Parent Groups:", true), - LIST_TRACKS("&b{}'s Tracks:" + "\n" + "{}", true), + PARENT_INFO("&b{}'s Parents: &7(showing page &f{}&7 of &f{}&7 - &f{}&7 entries)", true), + PARENT_INFO_NO_DATA("&b{}&a does not have any parents defined.", true), + + LIST_TRACKS("&b{}'s Tracks:", true), + LIST_TRACKS_ENTRY("&a{}: {}", false), LIST_TRACKS_EMPTY("&b{}&a is not on any tracks.", true), CONTEXT_PAIR_INLINE("&3{}=&b{}", false), diff --git a/common/src/main/java/me/lucko/luckperms/common/model/Group.java b/common/src/main/java/me/lucko/luckperms/common/model/Group.java index 7eecca03b..eb2fc4ff1 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/Group.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/Group.java @@ -37,6 +37,7 @@ import me.lucko.luckperms.common.caching.GroupCachedData; import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.references.GroupReference; +import me.lucko.luckperms.common.references.HolderType; import me.lucko.luckperms.common.references.Identifiable; import java.util.Optional; @@ -111,6 +112,11 @@ public class Group extends PermissionHolder implements Identifiable { return GroupReference.of(getId()); } + @Override + public HolderType getType() { + return HolderType.GROUP; + } + private CompletableFuture reloadCachedData() { return CompletableFuture.allOf(cachedData.reloadPermissions(), cachedData.reloadMeta()).thenAccept(n -> { getPlugin().getApiProvider().getEventFactory().handleGroupDataRecalculate(this, cachedData); diff --git a/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java b/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java index eb10e8605..dcde8632a 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java @@ -64,6 +64,7 @@ import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.primarygroup.GroupInheritanceComparator; import me.lucko.luckperms.common.references.GroupReference; import me.lucko.luckperms.common.references.HolderReference; +import me.lucko.luckperms.common.references.HolderType; import java.util.ArrayList; import java.util.Collection; @@ -240,7 +241,7 @@ public abstract class PermissionHolder { protected void declareState() { /* only declare state of groups. the state manager isn't really being used now the caches in this class are gone, but it's useful for command output. */ - if (this instanceof Group) { + if (this.getType().isGroup()) { plugin.getCachedStateManager().putAll(toReference(), getGroupReferences()); } } @@ -268,6 +269,13 @@ public abstract class PermissionHolder { */ public abstract HolderReference toReference(); + /** + * Returns the type of this PermissionHolder. + * + * @return this holders type + */ + public abstract HolderType getType(); + /** * Gets the API delegate for this instance * @@ -543,7 +551,7 @@ public abstract class PermissionHolder { excludedGroups = new HashSet<>(); } - if (this instanceof Group) { + if (this.getType().isGroup()) { excludedGroups.add(getObjectName().toLowerCase()); } @@ -619,7 +627,7 @@ public abstract class PermissionHolder { excludedGroups = new HashSet<>(); } - if (this instanceof Group) { + if (this.getType().isGroup()) { excludedGroups.add(getObjectName().toLowerCase()); } @@ -768,7 +776,7 @@ public abstract class PermissionHolder { excludedGroups = new HashSet<>(); } - if (this instanceof Group) { + if (this.getType().isGroup()) { excludedGroups.add(getObjectName().toLowerCase()); } @@ -830,7 +838,7 @@ public abstract class PermissionHolder { excludedGroups = new HashSet<>(); } - if (this instanceof Group) { + if (this.getType().isGroup()) { excludedGroups.add(getObjectName().toLowerCase()); } @@ -956,7 +964,7 @@ public abstract class PermissionHolder { * @return a tristate */ public Tristate hasPermission(Node node, boolean checkTransient) { - if (this instanceof Group && node.isGroupNode() && node.getGroupName().equalsIgnoreCase(getObjectName())) { + if (this.getType().isGroup() && node.isGroupNode() && node.getGroupName().equalsIgnoreCase(getObjectName())) { return Tristate.TRUE; } @@ -1327,7 +1335,7 @@ public abstract class PermissionHolder { nodesLock.unlock(); } - if (this instanceof User && giveDefault) { + if (this.getType().isUser() && giveDefault) { plugin.getUserManager().giveDefaultIfNeeded((User) this, false); } @@ -1355,7 +1363,7 @@ public abstract class PermissionHolder { nodesLock.unlock(); } - if (this instanceof User && giveDefault) { + if (this.getType().isUser() && giveDefault) { plugin.getUserManager().giveDefaultIfNeeded((User) this, false); } invalidateCache(); @@ -1501,7 +1509,7 @@ public abstract class PermissionHolder { } private OptionalInt calculateWeight() { - if (this instanceof User) return OptionalInt.empty(); + if (this.getType().isUser()) return OptionalInt.empty(); boolean seen = false; int best = 0; diff --git a/common/src/main/java/me/lucko/luckperms/common/model/User.java b/common/src/main/java/me/lucko/luckperms/common/model/User.java index f17a63850..74ae6d196 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/User.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/User.java @@ -36,6 +36,7 @@ import me.lucko.luckperms.common.caching.UserCachedData; import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.primarygroup.PrimaryGroupHolder; +import me.lucko.luckperms.common.references.HolderType; import me.lucko.luckperms.common.references.Identifiable; import me.lucko.luckperms.common.references.UserIdentifier; import me.lucko.luckperms.common.references.UserReference; @@ -168,6 +169,11 @@ public class User extends PermissionHolder implements Identifiable { private static final Comparator NULL_ORIGIN = new GroupInheritanceComparator(null); public static Comparator getFor(PermissionHolder origin) { - if (origin instanceof User) { + if (origin.getType().isUser()) { return new GroupInheritanceComparator(((User) origin)); } return NULL_ORIGIN; diff --git a/common/src/main/java/me/lucko/luckperms/common/references/HolderType.java b/common/src/main/java/me/lucko/luckperms/common/references/HolderType.java index d8561f7b1..7e3479fee 100644 --- a/common/src/main/java/me/lucko/luckperms/common/references/HolderType.java +++ b/common/src/main/java/me/lucko/luckperms/common/references/HolderType.java @@ -25,9 +25,27 @@ package me.lucko.luckperms.common.references; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import me.lucko.luckperms.common.model.PermissionHolder; + +@Getter +@AllArgsConstructor public enum HolderType { - USER, - GROUP + USER(true, false), + GROUP(false, true); + private final boolean user; + private final boolean group; + + public boolean matches(PermissionHolder holder) { + return holder.getType() == this; + } + + @Override + public String toString() { + return name().toLowerCase(); + } } diff --git a/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorUtils.java b/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorUtils.java index 8894be648..d1dfa6608 100644 --- a/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorUtils.java +++ b/common/src/main/java/me/lucko/luckperms/common/webeditor/WebEditorUtils.java @@ -70,7 +70,7 @@ public class WebEditorUtils { private static final String GROUP_ID_PATTERN = "group/"; public static String getHolderIdentifier(PermissionHolder holder) { - if (holder instanceof User) { + if (holder.getType().isUser()) { User user = ((User) holder); return USER_ID_PATTERN + user.getUuid().toString(); } else { diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/migration/SpongeMigrationUtils.java b/sponge/src/main/java/me/lucko/luckperms/sponge/migration/SpongeMigrationUtils.java index 47190eec3..841e0f9c2 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/migration/SpongeMigrationUtils.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/migration/SpongeMigrationUtils.java @@ -48,7 +48,7 @@ import java.util.Set; public class SpongeMigrationUtils { public static void migrateSubject(Subject from, PermissionHolder to, int priority) { - if (to instanceof Group) { + if (to.getType().isGroup()) { MigrationUtils.setGroupWeight((Group) to, priority); } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsSubjectData.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsSubjectData.java index 8958fefb5..0fffe1bf9 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsSubjectData.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsSubjectData.java @@ -133,7 +133,7 @@ public class LuckPermsSubjectData implements LPSubjectData { return CompletableFuture.completedFuture(false); } - if (holder instanceof User) { + if (holder.getType().isUser()) { service.getPlugin().getUserManager().giveDefaultIfNeeded(((User) holder), false); } @@ -158,7 +158,7 @@ public class LuckPermsSubjectData implements LPSubjectData { return CompletableFuture.completedFuture(false); } - if (holder instanceof User) { + if (holder.getType().isUser()) { service.getPlugin().getUserManager().giveDefaultIfNeeded(((User) holder), false); } @@ -251,7 +251,7 @@ public class LuckPermsSubjectData implements LPSubjectData { toRemove.forEach(makeUnsetConsumer(false)); ret = !toRemove.isEmpty(); - if (ret && holder instanceof User) { + if (ret && holder.getType().isUser()) { service.getPlugin().getUserManager().giveDefaultIfNeeded(((User) holder), false); } } @@ -277,7 +277,7 @@ public class LuckPermsSubjectData implements LPSubjectData { toRemove.forEach(makeUnsetConsumer(false)); ret = !toRemove.isEmpty(); - if (ret && holder instanceof User) { + if (ret && holder.getType().isUser()) { service.getPlugin().getUserManager().giveDefaultIfNeeded(((User) holder), false); } } @@ -441,14 +441,14 @@ public class LuckPermsSubjectData implements LPSubjectData { private CompletableFuture objectSave(PermissionHolder t) { if (!enduring) { // don't bother saving to primary storage. just refresh - if (t instanceof User) { + if (t.getType().isUser()) { User user = ((User) t); return user.getRefreshBuffer().request(); } else { return service.getPlugin().getUpdateTaskBuffer().request(); } } else { - if (t instanceof User) { + if (t.getType().isUser()) { User user = ((User) t); return service.getPlugin().getStorage().saveUser(user).thenComposeAsync(success -> { if (!success) {