From 9dbfa76b26f8ff8bc9248359dde02534cf9862d2 Mon Sep 17 00:00:00 2001 From: Luck Date: Mon, 22 Aug 2016 20:59:52 +0100 Subject: [PATCH] Add Vault chat support using permission nodes --- .../luckperms/api/vault/VaultChatHook.java | 303 +++++++++++++++--- .../lucko/luckperms/api/vault/VaultHook.java | 3 +- .../api/vault/VaultPermissionHook.java | 2 +- .../luckperms/core/PermissionHolder.java | 66 ++-- 4 files changed, 306 insertions(+), 68 deletions(-) diff --git a/bukkit/src/main/java/me/lucko/luckperms/api/vault/VaultChatHook.java b/bukkit/src/main/java/me/lucko/luckperms/api/vault/VaultChatHook.java index 1b7559ee..b1520e57 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/api/vault/VaultChatHook.java +++ b/bukkit/src/main/java/me/lucko/luckperms/api/vault/VaultChatHook.java @@ -22,128 +22,359 @@ package me.lucko.luckperms.api.vault; +import lombok.Setter; +import me.lucko.luckperms.LPBukkitPlugin; +import me.lucko.luckperms.constants.Patterns; +import me.lucko.luckperms.core.PermissionHolder; +import me.lucko.luckperms.exceptions.ObjectAlreadyHasException; +import me.lucko.luckperms.groups.Group; +import me.lucko.luckperms.users.User; import net.milkbowl.vault.chat.Chat; -import net.milkbowl.vault.permission.Permission; + +import java.util.Collections; +import java.util.Map; +import java.util.regex.Pattern; /** - * Dummy class for hooking with Vault plugins that need both Chat + Perms - * This doesn't return anything useful, or change anything internally + * Provides the Vault Chat service through the use of normal permission nodes. * - * Registered on the lowest priority so other plugins can override + * Prefixes / Suffixes: + * Normal inheritance rules apply. + * Permission Nodes = prefix.priority.value OR suffix.priority.value + * If a user/group has / inherits multiple prefixes and suffixes, the one with the highest priority is the one that + * will apply. + * + * Meta: + * Normal inheritance rules DO NOT apply. + * Permission Nodes = meta.node.value + * + * Node that special characters used within LuckPerms are escaped: + * See {@link #escapeCharacters(String)} + * + * Registered on normal priority so other plugins can override. */ class VaultChatHook extends Chat { + private static final Pattern PREFIX_PATTERN = Pattern.compile("(?i)prefix\\.\\d+\\..*"); + private static final Pattern SUFFIX_PATTERN = Pattern.compile("(?i)suffix\\.\\d+\\..*"); - VaultChatHook(Permission perms) { + @Setter + private LPBukkitPlugin plugin; + private final VaultPermissionHook perms; + + VaultChatHook(VaultPermissionHook perms) { super(perms); - } - - private void throwNotSupported() { - // throw new UnsupportedOperationException("LuckPerms cannot perform this operation."); + this.perms = perms; } public String getName() { - return "LuckPerms"; + return perms.getName(); } - public boolean isEnabled() { - return true; + return perms.isEnabled(); + } + + private static String escapeCharacters(String s) { + s = s.replace(".", "{SEP}"); + s = s.replace("/", "{FSEP}"); + s = s.replace("$", "{DSEP}"); + + return s; + } + + private void saveMeta(PermissionHolder holder, String world, String node, String value) { + if (holder == null) return; + node = escapeCharacters(node); + value = escapeCharacters(value); + + if (world == null || world.equals("")) { + try { + holder.setPermission("meta." + node + "." + value, true); + } catch (ObjectAlreadyHasException ignored) {} + + } else { + try { + holder.setPermission("meta." + node + "." + value, true, "global", world); + } catch (ObjectAlreadyHasException ignored) {} + } + + perms.objectSave(holder); + } + + private static int getMeta(PermissionHolder holder, String world, String node, int defaultValue) { + if (holder == null) return defaultValue; + node = escapeCharacters(node); + + for (Map.Entry e : holder.getPermissions("global", world, null, true, Collections.emptyList(), false).entrySet()) { + if (!e.getValue()) continue; + + String[] parts = Patterns.DOT.split(e.getKey(), 3); + if (parts.length < 3) continue; + + if (!parts[0].equalsIgnoreCase("meta")) { + continue; + } + + if (!parts[1].equalsIgnoreCase(node)) { + continue; + } + + try { + return Integer.parseInt(parts[2]); + } catch (Throwable t) { + return defaultValue; + } + } + + return defaultValue; + } + + private static double getMeta(PermissionHolder holder, String world, String node, double defaultValue) { + if (holder == null) return defaultValue; + node = escapeCharacters(node); + + for (Map.Entry e : holder.getPermissions("global", world, null, true, Collections.emptyList(), false).entrySet()) { + if (!e.getValue()) continue; + + String[] parts = Patterns.DOT.split(e.getKey(), 3); + if (parts.length < 3) continue; + + if (!parts[0].equalsIgnoreCase("meta")) { + continue; + } + + if (!parts[1].equalsIgnoreCase(node)) { + continue; + } + + try { + return Double.parseDouble(parts[2]); + } catch (Throwable t) { + return defaultValue; + } + } + + return defaultValue; + } + + private static boolean getMeta(PermissionHolder holder, String world, String node, boolean defaultValue) { + if (holder == null) return defaultValue; + + node = escapeCharacters(node); + + for (Map.Entry e : holder.getPermissions("global", world, null, true, Collections.emptyList(), false).entrySet()) { + if (!e.getValue()) continue; + + String[] parts = Patterns.DOT.split(e.getKey(), 3); + if (parts.length < 3) continue; + + if (!parts[0].equalsIgnoreCase("meta")) { + continue; + } + + if (!parts[1].equalsIgnoreCase(node)) { + continue; + } + + try { + return Boolean.parseBoolean(parts[2]); + } catch (Throwable t) { + return defaultValue; + } + } + + return defaultValue; + } + + private static String getMeta(PermissionHolder holder, String world, String node, String defaultValue) { + if (holder == null) return defaultValue; + + node = escapeCharacters(node); + + for (Map.Entry e : holder.getPermissions("global", world, null, true, Collections.emptyList(), false).entrySet()) { + if (!e.getValue()) continue; + + String[] parts = Patterns.DOT.split(e.getKey(), 3); + if (parts.length < 3) continue; + + if (!parts[0].equalsIgnoreCase("meta")) { + continue; + } + + if (!parts[1].equalsIgnoreCase(node)) { + continue; + } + + return parts[2]; + } + + return defaultValue; + } + + private static String getChatMeta(Pattern pattern, PermissionHolder holder, String world) { + if (holder == null) return ""; + + int priority = 0; + String meta = null; + for (Map.Entry e : holder.getLocalPermissions("global", world, null).entrySet()) { + if (!e.getValue()) continue; + + if (pattern.matcher(e.getKey()).matches()) { + String[] parts = Patterns.DOT.split(e.getKey(), 3); + int p = Integer.parseInt(parts[1]); + + if (meta == null || p > priority) { + meta = parts[2]; + priority = p; + } + } + } + + return meta == null ? "" : meta; } public String getPlayerPrefix(String world, String player) { - return ""; + final User user = plugin.getUserManager().get(player); + return getChatMeta(PREFIX_PATTERN, user, world); } public void setPlayerPrefix(String world, String player, String prefix) { - throwNotSupported(); + final User user = plugin.getUserManager().get(player); + if (user == null) return; + + try { + user.setPermission("prefix.1000." + escapeCharacters(prefix), true); + } catch (ObjectAlreadyHasException ignored) {} + + perms.objectSave(user); } public String getPlayerSuffix(String world, String player) { - return ""; + final User user = plugin.getUserManager().get(player); + return getChatMeta(SUFFIX_PATTERN, user, world); } public void setPlayerSuffix(String world, String player, String suffix) { - throwNotSupported(); + final User user = plugin.getUserManager().get(player); + if (user == null) return; + + try { + user.setPermission("suffix.1000." + escapeCharacters(suffix), true); + } catch (ObjectAlreadyHasException ignored) {} + + perms.objectSave(user); } public String getGroupPrefix(String world, String group) { - return ""; + final Group g = plugin.getGroupManager().get(group); + return getChatMeta(PREFIX_PATTERN, g, world); } public void setGroupPrefix(String world, String group, String prefix) { - throwNotSupported(); + final Group g = plugin.getGroupManager().get(group); + if (g == null) return; + + try { + g.setPermission("prefix.1000." + escapeCharacters(prefix), true); + } catch (ObjectAlreadyHasException ignored) {} + + perms.objectSave(g); } public String getGroupSuffix(String world, String group) { - return ""; + final Group g = plugin.getGroupManager().get(group); + return getChatMeta(SUFFIX_PATTERN, g, world); } public void setGroupSuffix(String world, String group, String suffix) { - throwNotSupported(); + final Group g = plugin.getGroupManager().get(group); + if (g == null) return; + + try { + g.setPermission("suffix.1000." + escapeCharacters(suffix), true); + } catch (ObjectAlreadyHasException ignored) {} + + perms.objectSave(g); } public int getPlayerInfoInteger(String world, String player, String node, int defaultValue) { - return defaultValue; + final User user = plugin.getUserManager().get(player); + return getMeta(user, world, node, defaultValue); } public void setPlayerInfoInteger(String world, String player, String node, int value) { - throwNotSupported(); + final User user = plugin.getUserManager().get(player); + saveMeta(user, world, node, String.valueOf(value)); } public int getGroupInfoInteger(String world, String group, String node, int defaultValue) { - return defaultValue; + final Group g = plugin.getGroupManager().get(group); + return getMeta(g, world, node, defaultValue); } public void setGroupInfoInteger(String world, String group, String node, int value) { - throwNotSupported(); + final Group g = plugin.getGroupManager().get(group); + saveMeta(g, world, node, String.valueOf(value)); } public double getPlayerInfoDouble(String world, String player, String node, double defaultValue) { - return defaultValue; + final User user = plugin.getUserManager().get(player); + return getMeta(user, world, node, defaultValue); } public void setPlayerInfoDouble(String world, String player, String node, double value) { - throwNotSupported(); + final User user = plugin.getUserManager().get(player); + saveMeta(user, world, node, String.valueOf(value)); } public double getGroupInfoDouble(String world, String group, String node, double defaultValue) { - return defaultValue; + final Group g = plugin.getGroupManager().get(group); + return getMeta(g, world, node, defaultValue); } public void setGroupInfoDouble(String world, String group, String node, double value) { - throwNotSupported(); + final Group g = plugin.getGroupManager().get(group); + saveMeta(g, world, node, String.valueOf(value)); } public boolean getPlayerInfoBoolean(String world, String player, String node, boolean defaultValue) { - return defaultValue; + final User user = plugin.getUserManager().get(player); + return getMeta(user, world, node, defaultValue); } public void setPlayerInfoBoolean(String world, String player, String node, boolean value) { - throwNotSupported(); + final User user = plugin.getUserManager().get(player); + saveMeta(user, world, node, String.valueOf(value)); } public boolean getGroupInfoBoolean(String world, String group, String node, boolean defaultValue) { - return defaultValue; + final Group g = plugin.getGroupManager().get(group); + return getMeta(g, world, node, defaultValue); } public void setGroupInfoBoolean(String world, String group, String node, boolean value) { - throwNotSupported(); + final Group g = plugin.getGroupManager().get(group); + saveMeta(g, world, node, String.valueOf(value)); } public String getPlayerInfoString(String world, String player, String node, String defaultValue) { - return defaultValue; + final User user = plugin.getUserManager().get(player); + return getMeta(user, world, node, defaultValue); } public void setPlayerInfoString(String world, String player, String node, String value) { - throwNotSupported(); + final User user = plugin.getUserManager().get(player); + saveMeta(user, world, node, value); } public String getGroupInfoString(String world, String group, String node, String defaultValue) { - return defaultValue; + final Group g = plugin.getGroupManager().get(group); + return getMeta(g, world, node, defaultValue); } public void setGroupInfoString(String world, String group, String node, String value) { - throwNotSupported(); + final Group g = plugin.getGroupManager().get(group); + saveMeta(g, world, node, value); } } diff --git a/bukkit/src/main/java/me/lucko/luckperms/api/vault/VaultHook.java b/bukkit/src/main/java/me/lucko/luckperms/api/vault/VaultHook.java index ee549c6c..09f69446 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/api/vault/VaultHook.java +++ b/bukkit/src/main/java/me/lucko/luckperms/api/vault/VaultHook.java @@ -43,11 +43,12 @@ public class VaultHook { if (chatHook == null) { chatHook = new VaultChatHook(permissionHook); } + chatHook.setPlugin(plugin); final ServicesManager sm = plugin.getServer().getServicesManager(); sm.unregisterAll(plugin); sm.register(Permission.class, permissionHook, plugin, ServicePriority.High); - sm.register(Chat.class, chatHook, plugin, ServicePriority.Lowest); + sm.register(Chat.class, chatHook, plugin, ServicePriority.Low); } catch (Exception e) { e.printStackTrace(); diff --git a/bukkit/src/main/java/me/lucko/luckperms/api/vault/VaultPermissionHook.java b/bukkit/src/main/java/me/lucko/luckperms/api/vault/VaultPermissionHook.java index 2cc0dc52..555b9145 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/api/vault/VaultPermissionHook.java +++ b/bukkit/src/main/java/me/lucko/luckperms/api/vault/VaultPermissionHook.java @@ -93,7 +93,7 @@ class VaultPermissionHook extends Permission { return true; } - private void objectSave(PermissionHolder t) { + void objectSave(PermissionHolder t) { if (t instanceof User) { ((User) t).refreshPermissions(); plugin.getDatastore().saveUser(((User) t), Callback.empty()); diff --git a/common/src/main/java/me/lucko/luckperms/core/PermissionHolder.java b/common/src/main/java/me/lucko/luckperms/core/PermissionHolder.java index 5b92834d..6c811d3c 100644 --- a/common/src/main/java/me/lucko/luckperms/core/PermissionHolder.java +++ b/common/src/main/java/me/lucko/luckperms/core/PermissionHolder.java @@ -566,7 +566,11 @@ public abstract class PermissionHolder { return nodes; } - protected Map getPermissions(String server, String world, List excludedGroups, boolean includeGlobal, List possibleNodes) { + public Map getPermissions(String server, String world, List excludedGroups, boolean includeGlobal, List possibleNodes) { + return getPermissions(server, world, excludedGroups, includeGlobal, possibleNodes, true); + } + + public Map getPermissions(String server, String world, List excludedGroups, boolean includeGlobal, List possibleNodes, boolean applyGroups) { if (excludedGroups == null) { excludedGroups = new ArrayList<>(); } @@ -686,40 +690,16 @@ public abstract class PermissionHolder { groupNodes.remove(Patterns.SERVER_DELIMITER.split(node.getKey(), 2)[1]); }); - // Apply lowest priority: groupNodes - for (Map.Entry groupNode : groupNodes.entrySet()) { - // Add the actual group perm node, so other plugins can hook - perms.put(groupNode.getKey(), groupNode.getValue()); - - // Don't add negated groups - if (!groupNode.getValue()) continue; - - String groupName = Patterns.DOT.split(groupNode.getKey(), 2)[1]; - if (!excludedGroups.contains(groupName)) { - Group group = plugin.getGroupManager().get(groupName); - if (group != null) { - perms.putAll(group.getLocalPermissions(server, excludedGroups)); - } else { - plugin.getLog().warn("Error whilst refreshing the permissions of '" + objectName + "'." + - "\n The group '" + groupName + "' is not loaded."); - } - } - } - - applyShorthandIfEnabled(perms); - - // Apply next priorities: serverSpecificGroups and then serverWorldSpecificGroups - for (Map m : Arrays.asList(serverSpecificGroups, serverWorldSpecificGroups)) { - for (Map.Entry groupNode : m.entrySet()) { - final String rawNode = Patterns.SERVER_DELIMITER.split(groupNode.getKey())[1]; - + if (applyGroups) { + // Apply lowest priority: groupNodes + for (Map.Entry groupNode : groupNodes.entrySet()) { // Add the actual group perm node, so other plugins can hook - perms.put(rawNode, groupNode.getValue()); + perms.put(groupNode.getKey(), groupNode.getValue()); // Don't add negated groups if (!groupNode.getValue()) continue; - String groupName = Patterns.DOT.split(rawNode, 2)[1]; + String groupName = Patterns.DOT.split(groupNode.getKey(), 2)[1]; if (!excludedGroups.contains(groupName)) { Group group = plugin.getGroupManager().get(groupName); if (group != null) { @@ -730,7 +710,33 @@ public abstract class PermissionHolder { } } } + applyShorthandIfEnabled(perms); + + // Apply next priorities: serverSpecificGroups and then serverWorldSpecificGroups + for (Map m : Arrays.asList(serverSpecificGroups, serverWorldSpecificGroups)) { + for (Map.Entry groupNode : m.entrySet()) { + final String rawNode = Patterns.SERVER_DELIMITER.split(groupNode.getKey())[1]; + + // Add the actual group perm node, so other plugins can hook + perms.put(rawNode, groupNode.getValue()); + + // Don't add negated groups + if (!groupNode.getValue()) continue; + + String groupName = Patterns.DOT.split(rawNode, 2)[1]; + if (!excludedGroups.contains(groupName)) { + Group group = plugin.getGroupManager().get(groupName); + if (group != null) { + perms.putAll(group.getLocalPermissions(server, excludedGroups)); + } else { + plugin.getLog().warn("Error whilst refreshing the permissions of '" + objectName + "'." + + "\n The group '" + groupName + "' is not loaded."); + } + } + } + applyShorthandIfEnabled(perms); + } } // Apply next priority: userNodes