From 5a34fa182a659aad5c29f127cb1bc2c7bd311cf1 Mon Sep 17 00:00:00 2001 From: Luck Date: Sat, 23 Dec 2017 18:25:23 +0000 Subject: [PATCH] rewrite Vault implementation to upgrade requests to uuids instead of downgrade to usernames --- .../bukkit/vault/AbstractVaultChat.java | 591 ++++++++++++++++++ .../bukkit/vault/AbstractVaultPermission.java | 335 ++++++++++ .../luckperms/bukkit/vault/VaultChatHook.java | 443 +++++++------ .../luckperms/bukkit/vault/VaultExecutor.java | 70 --- .../bukkit/vault/VaultHookManager.java | 2 +- .../bukkit/vault/VaultPermissionHook.java | 511 ++++++--------- 6 files changed, 1335 insertions(+), 617 deletions(-) create mode 100644 bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/AbstractVaultChat.java create mode 100644 bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/AbstractVaultPermission.java delete mode 100644 bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/VaultExecutor.java diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/AbstractVaultChat.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/AbstractVaultChat.java new file mode 100644 index 000000000..7ebd3da6a --- /dev/null +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/AbstractVaultChat.java @@ -0,0 +1,591 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * 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.bukkit.vault; + +import net.milkbowl.vault.chat.Chat; +import net.milkbowl.vault.permission.Permission; + +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.World; +import org.bukkit.entity.Player; + +import java.util.UUID; +import java.util.function.Function; + +/** + * An extended abstraction of the Vault {@link Chat} API. + * + * The original Vault API only contained methods to query data by username. Over + * time, the maintainers added additional methods to query by (Offline)Player, but + * in order to keep backwards compatibility with implementations which only supported + * usernames, they implemented the Player query methods and downgraded the requests + * to get a result using the players username. + * + * Whilst this meant the old plugins would still be supported, it made the whole + * API a total mess. This class reverses this decision, and instead upgrades + * requests to use UUIDs. This makes implementing Vault significantly easier for modern + * plugins, and because requests are upgraded instead of being downgraded then upgraded, + * much faster for plugins querying data. + */ +@SuppressWarnings("deprecation") +public abstract class AbstractVaultChat extends Chat { + + // when upgrading and forwarding requests, all world strings are passed through this function. + // it lets the overriding class define some custom behaviour for world handling. + protected Function worldMappingFunction = Function.identity(); + + // the permission api instance + private final Permission permissionApi; + + public AbstractVaultChat(Permission permissionApi) { + super(permissionApi); + this.permissionApi = permissionApi; + } + + @Override + public boolean isEnabled() { + // always return true + return true; + } + + // methods subclasses are expected to implement + public abstract String getPlayerPrefix(String world, UUID uuid); + public abstract String getPlayerSuffix(String world, UUID uuid); + public abstract void setPlayerPrefix(String world, UUID uuid, String prefix); + public abstract void setPlayerSuffix(String world, UUID uuid, String suffix); + public abstract String getPlayerInfo(String world, UUID uuid, String key); + public abstract void setPlayerInfo(String world, UUID uuid, String key, Object value); + public abstract String getGroupsPrefix(String world, String name); // note "groups" not "group" + public abstract String getGroupsSuffix(String world, String name); // note "groups" not "group" + public abstract void setGroupsPrefix(String world, String name, String prefix); // note "groups" not "group" + public abstract void setGroupsSuffix(String world, String name, String suffix); // note "groups" not "group" + public abstract String getGroupInfo(String world, String name, String key); + public abstract void setGroupInfo(String world, String name, String key, Object value); + + // utility methods for parsing metadata values from strings + + private static String strConvert(String s, String def) { + if (s != null) { + return s; + } + return def; + } + + private static int intConvert(String s, int def) { + if (s == null) { + return def; + } + try { + return Integer.parseInt(s); + } catch (NumberFormatException e) { + return def; + } + } + + private static double doubleConvert(String s, double def) { + if (s == null) { + return def; + } + try { + return Double.parseDouble(s); + } catch (NumberFormatException e) { + return def; + } + } + + private static boolean booleanConvert(String s, boolean def) { + if (s == null) { + return def; + } + if (s.equalsIgnoreCase("true")) { + return true; + } else if (s.equalsIgnoreCase("false")) { + return false; + } + return def; + } + + // utility methods for upgrading legacy requests + + private static UUID player(String player) { + if (player == null) { + return null; + } + return player(Bukkit.getOfflinePlayer(player)); + } + + private static UUID player(OfflinePlayer player) { + if (player == null) { + return null; + } + return player.getUniqueId(); + } + + private String world(String world) { + return worldMappingFunction.apply(world); + } + + private String world(Player player) { + if (player == null) { + return null; + } + return world(player.getWorld()); + } + + private String world(World world) { + if (world == null) { + return null; + } + return world(world.getName()); + } + + @Override + public String getPlayerPrefix(String world, String player) { + return getPlayerPrefix(world(world), player(player)); + } + + @Override + public String getPlayerPrefix(String world, OfflinePlayer player) { + return getPlayerPrefix(world(world), player(player)); + } + + @Override + public String getPlayerPrefix(World world, String player) { + return getPlayerPrefix(world(world), player(player)); + } + + @Override + public String getPlayerPrefix(Player player) { + return getPlayerPrefix(world(player), player(player)); + } + + @Override + public void setPlayerPrefix(String world, String player, String prefix) { + setPlayerPrefix(world(world), player(player), prefix); + } + + @Override + public void setPlayerPrefix(String world, OfflinePlayer player, String prefix) { + setPlayerPrefix(world(world), player(player), prefix); + } + + @Override + public void setPlayerPrefix(World world, String player, String prefix) { + setPlayerPrefix(world(world), player(player), prefix); + } + + @Override + public void setPlayerPrefix(Player player, String prefix) { + setPlayerPrefix(world(player), player(player), prefix); + } + + @Override + public String getPlayerSuffix(String world, String player) { + return getPlayerSuffix(world(world), player(player)); + } + + @Override + public String getPlayerSuffix(String world, OfflinePlayer player) { + return getPlayerSuffix(world(world), player(player)); + } + + @Override + public String getPlayerSuffix(World world, String player) { + return getPlayerSuffix(world(world), player(player)); + } + + @Override + public String getPlayerSuffix(Player player) { + return getPlayerSuffix(world(player), player(player)); + } + + @Override + public void setPlayerSuffix(String world, String player, String suffix) { + setPlayerSuffix(world(world), player(player), suffix); + } + + @Override + public void setPlayerSuffix(String world, OfflinePlayer player, String suffix) { + setPlayerSuffix(world(world), player(player), suffix); + } + + @Override + public void setPlayerSuffix(World world, String player, String suffix) { + setPlayerSuffix(world(world), player(player), suffix); + } + + @Override + public void setPlayerSuffix(Player player, String suffix) { + setPlayerSuffix(world(player), player(player), suffix); + } + + @Override + public String getGroupPrefix(String world, String group) { + return getGroupsPrefix(world(world), group); + } + + @Override + public String getGroupPrefix(World world, String group) { + return getGroupsPrefix(world(world), group); + } + + @Override + public void setGroupPrefix(String world, String group, String prefix) { + setGroupsPrefix(world(world), group, prefix); + } + + @Override + public void setGroupPrefix(World world, String group, String prefix) { + setGroupsPrefix(world(world), group, prefix); + } + + @Override + public String getGroupSuffix(String world, String group) { + return getGroupsSuffix(world(world), group); + } + + @Override + public String getGroupSuffix(World world, String group) { + return getGroupsSuffix(world(world), group); + } + + @Override + public void setGroupSuffix(String world, String group, String suffix) { + setGroupsSuffix(world(world), group, suffix); + } + + @Override + public void setGroupSuffix(World world, String group, String suffix) { + setGroupsSuffix(world(world), group, suffix); + } + + @Override + public int getPlayerInfoInteger(String world, OfflinePlayer player, String node, int defaultValue) { + return intConvert(getPlayerInfo(world(world), player(player), node), defaultValue); + } + + @Override + public int getPlayerInfoInteger(String world, String player, String node, int defaultValue) { + return intConvert(getPlayerInfo(world(world), player(player), node), defaultValue); + } + + @Override + public int getPlayerInfoInteger(World world, String player, String node, int defaultValue) { + return intConvert(getPlayerInfo(world(world), player(player), node), defaultValue); + } + + @Override + public int getPlayerInfoInteger(Player player, String node, int defaultValue) { + return intConvert(getPlayerInfo(world(player), player(player), node), defaultValue); + } + + @Override + public void setPlayerInfoInteger(String world, OfflinePlayer player, String node, int value) { + setPlayerInfo(world(world), player(player), node, value); + } + + @Override + public void setPlayerInfoInteger(String world, String player, String node, int value) { + setPlayerInfo(world(world), player(player), node, value); + } + + @Override + public void setPlayerInfoInteger(World world, String player, String node, int value) { + setPlayerInfo(world(world), player(player), node, value); + } + + @Override + public void setPlayerInfoInteger(Player player, String node, int value) { + setPlayerInfo(world(player), player(player), node, value); + } + + @Override + public int getGroupInfoInteger(String world, String group, String node, int defaultValue) { + return intConvert(getGroupInfo(world(world), group, node), defaultValue); + } + + @Override + public int getGroupInfoInteger(World world, String group, String node, int defaultValue) { + return intConvert(getGroupInfo(world(world), group, node), defaultValue); + } + + @Override + public void setGroupInfoInteger(String world, String group, String node, int value) { + setGroupInfo(world(world), group, node, value); + } + + @Override + public void setGroupInfoInteger(World world, String group, String node, int value) { + setGroupInfo(world(world), group, node, value); + } + + @Override + public double getPlayerInfoDouble(String world, OfflinePlayer player, String node, double defaultValue) { + return doubleConvert(getPlayerInfo(world(world), player(player), node), defaultValue); + } + + @Override + public double getPlayerInfoDouble(String world, String player, String node, double defaultValue) { + return doubleConvert(getPlayerInfo(world(world), player(player), node), defaultValue); + } + + @Override + public double getPlayerInfoDouble(World world, String player, String node, double defaultValue) { + return doubleConvert(getPlayerInfo(world(world), player(player), node), defaultValue); + } + + @Override + public double getPlayerInfoDouble(Player player, String node, double defaultValue) { + return doubleConvert(getPlayerInfo(world(player), player(player), node), defaultValue); + } + + @Override + public void setPlayerInfoDouble(String world, OfflinePlayer player, String node, double value) { + setPlayerInfo(world(world), player(player), node, value); + } + + @Override + public void setPlayerInfoDouble(String world, String player, String node, double value) { + setPlayerInfo(world(world), player(player), node, value); + } + + @Override + public void setPlayerInfoDouble(World world, String player, String node, double value) { + setPlayerInfo(world(world), player(player), node, value); + } + + @Override + public void setPlayerInfoDouble(Player player, String node, double value) { + setPlayerInfo(world(player), player(player), node, value); + } + + @Override + public double getGroupInfoDouble(String world, String group, String node, double defaultValue) { + return doubleConvert(getGroupInfo(world(world), group, node), defaultValue); + } + + @Override + public double getGroupInfoDouble(World world, String group, String node, double defaultValue) { + return doubleConvert(getGroupInfo(world(world), group, node), defaultValue); + } + + @Override + public void setGroupInfoDouble(String world, String group, String node, double value) { + setGroupInfo(world(world), group, node, value); + } + + @Override + public void setGroupInfoDouble(World world, String group, String node, double value) { + setGroupInfo(world(world), group, node, value); + } + + @Override + public boolean getPlayerInfoBoolean(String world, OfflinePlayer player, String node, boolean defaultValue) { + return booleanConvert(getPlayerInfo(world(world), player(player), node), defaultValue); + } + + @Override + public boolean getPlayerInfoBoolean(String world, String player, String node, boolean defaultValue) { + return booleanConvert(getPlayerInfo(world(world), player(player), node), defaultValue); + } + + @Override + public boolean getPlayerInfoBoolean(World world, String player, String node, boolean defaultValue) { + return booleanConvert(getPlayerInfo(world(world), player(player), node), defaultValue); + } + + @Override + public boolean getPlayerInfoBoolean(Player player, String node, boolean defaultValue) { + return booleanConvert(getPlayerInfo(world(player), player(player), node), defaultValue); + } + + @Override + public void setPlayerInfoBoolean(String world, OfflinePlayer player, String node, boolean value) { + setPlayerInfo(world(world), player(player), node, value); + } + + @Override + public void setPlayerInfoBoolean(String world, String player, String node, boolean value) { + setPlayerInfo(world(world), player(player), node, value); + } + + @Override + public void setPlayerInfoBoolean(World world, String player, String node, boolean value) { + setPlayerInfo(world(world), player(player), node, value); + } + + @Override + public void setPlayerInfoBoolean(Player player, String node, boolean value) { + setPlayerInfo(world(player), player(player), node, value); + } + + @Override + public boolean getGroupInfoBoolean(String world, String group, String node, boolean defaultValue) { + return booleanConvert(getGroupInfo(world(world), group, node), defaultValue); + } + + @Override + public boolean getGroupInfoBoolean(World world, String group, String node, boolean defaultValue) { + return booleanConvert(getGroupInfo(world(world), group, node), defaultValue); + } + + @Override + public void setGroupInfoBoolean(String world, String group, String node, boolean value) { + setGroupInfo(world(world), group, node, value); + } + + @Override + public void setGroupInfoBoolean(World world, String group, String node, boolean value) { + setGroupInfo(world(world), group, node, value); + } + + @Override + public String getPlayerInfoString(String world, OfflinePlayer player, String node, String defaultValue) { + return strConvert(getPlayerInfo(world(world), player(player), node), defaultValue); + } + + @Override + public String getPlayerInfoString(String world, String player, String node, String defaultValue) { + return strConvert(getPlayerInfo(world(world), player(player), node), defaultValue); + } + + @Override + public String getPlayerInfoString(World world, String player, String node, String defaultValue) { + return strConvert(getPlayerInfo(world(world), player(player), node), defaultValue); + } + + @Override + public String getPlayerInfoString(Player player, String node, String defaultValue) { + return strConvert(getPlayerInfo(world(player), player(player), node), defaultValue); + } + + @Override + public void setPlayerInfoString(String world, OfflinePlayer player, String node, String value) { + setPlayerInfo(world(world), player(player), node, value); + } + + @Override + public void setPlayerInfoString(String world, String player, String node, String value) { + setPlayerInfo(world(world), player(player), node, value); + } + + @Override + public void setPlayerInfoString(World world, String player, String node, String value) { + setPlayerInfo(world(world), player(player), node, value); + } + + @Override + public void setPlayerInfoString(Player player, String node, String value) { + setPlayerInfo(world(player), player(player), node, value); + } + + @Override + public String getGroupInfoString(String world, String group, String node, String defaultValue) { + return strConvert(getGroupInfo(world(world), group, node), defaultValue); + } + + @Override + public String getGroupInfoString(World world, String group, String node, String defaultValue) { + return strConvert(getGroupInfo(world(world), group, node), defaultValue); + } + + @Override + public void setGroupInfoString(String world, String group, String node, String value) { + setGroupInfo(world(world), group, node, value); + } + + @Override + public void setGroupInfoString(World world, String group, String node, String value) { + setGroupInfo(world(world), group, node, value); + } + + @Override + public boolean playerInGroup(String world, OfflinePlayer player, String group) { + return permissionApi.playerInGroup(world, player, group); + } + + @Override + public boolean playerInGroup(String world, String player, String group) { + return permissionApi.playerInGroup(world, player, group); + } + + @Override + public boolean playerInGroup(World world, String player, String group) { + return permissionApi.playerInGroup(world, player, group); + } + + @Override + public boolean playerInGroup(Player player, String group) { + return permissionApi.playerInGroup(player, group); + } + + @Override + public String[] getPlayerGroups(String world, OfflinePlayer player) { + return permissionApi.getPlayerGroups(world, player); + } + + @Override + public String[] getPlayerGroups(String world, String player) { + return permissionApi.getPlayerGroups(world, player); + } + + @Override + public String[] getPlayerGroups(World world, String player) { + return permissionApi.getPlayerGroups(world, player); + } + + @Override + public String[] getPlayerGroups(Player player) { + return permissionApi.getPlayerGroups(player); + } + + @Override + public String getPrimaryGroup(String world, OfflinePlayer player) { + return permissionApi.getPrimaryGroup(world, player); + } + + @Override + public String getPrimaryGroup(String world, String player) { + return permissionApi.getPrimaryGroup(world, player); + } + + @Override + public String getPrimaryGroup(World world, String player) { + return permissionApi.getPrimaryGroup(world, player); + } + + @Override + public String getPrimaryGroup(Player player) { + return permissionApi.getPrimaryGroup(player); + } + + @Override + public String[] getGroups() { + return permissionApi.getGroups(); + } + +} diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/AbstractVaultPermission.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/AbstractVaultPermission.java new file mode 100644 index 000000000..13cbd03b6 --- /dev/null +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/AbstractVaultPermission.java @@ -0,0 +1,335 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * 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.bukkit.vault; + +import net.milkbowl.vault.permission.Permission; + +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.plugin.java.JavaPlugin; + +import java.util.UUID; +import java.util.function.Function; + +/** + * An extended abstraction of the Vault {@link Permission} API. + * + * The original Vault API only contained methods to query data by username. Over + * time, the maintainers added additional methods to query by (Offline)Player, but + * in order to keep backwards compatibility with implementations which only supported + * usernames, they implemented the Player query methods and downgraded the requests + * to get a result using the players username. + * + * Whilst this meant the old plugins would still be supported, it made the whole + * API a total mess. This class reverses this decision, and instead upgrades + * requests to use UUIDs. This makes implementing Vault significantly easier for modern + * plugins, and because requests are upgraded instead of being downgraded then upgraded, + * much faster for plugins querying data. + */ +@SuppressWarnings("deprecation") +public abstract class AbstractVaultPermission extends Permission { + + // when upgrading and forwarding requests, all world strings are passed through this function. + // it lets the overriding class define some custom behaviour for world handling. + protected Function worldMappingFunction = Function.identity(); + + public AbstractVaultPermission() { + super.plugin = JavaPlugin.getProvidingPlugin(Permission.class); + } + + @Override + public boolean isEnabled() { + // always return true + return true; + } + + @Override + public boolean hasSuperPermsCompat() { + // always return true + return true; + } + + @Override + public boolean hasGroupSupport() { + // always return true + return true; + } + + // methods subclasses are expected to implement + public abstract boolean hasPermission(String world, UUID uuid, String permission); + public abstract boolean playerAddPermission(String world, UUID uuid, String permission); + public abstract boolean playerRemovePermission(String world, UUID uuid, String permission); + public abstract boolean playerInGroup(String world, UUID uuid, String group); + public abstract boolean playerAddGroup(String world, UUID uuid, String group); + public abstract boolean playerRemoveGroup(String world, UUID uuid, String group); + public abstract String[] playerGetGroups(String world, UUID uuid); + public abstract String playerPrimaryGroup(String world, UUID uuid); + public abstract boolean groupHasPermission(String world, String name, String permission); + public abstract boolean groupAddPermission(String world, String name, String permission); + public abstract boolean groupRemovePermission(String world, String name, String permission); + + // utility methods for upgrading legacy requests + + private static UUID player(String player) { + if (player == null) { + return null; + } + return player(Bukkit.getOfflinePlayer(player)); + } + + private static UUID player(OfflinePlayer player) { + if (player == null) { + return null; + } + return player.getUniqueId(); + } + + private String world(String world) { + return worldMappingFunction.apply(world); + } + + private String world(Player player) { + if (player == null) { + return null; + } + return world(player.getWorld()); + } + + private String world(World world) { + if (world == null) { + return null; + } + return world(world.getName()); + } + + @Override + public boolean has(String world, String player, String permission) { + return hasPermission(world(world), player(player), permission); + } + + @Override + public boolean has(World world, String player, String permission) { + return hasPermission(world(world), player(player), permission); + } + + @Override + public boolean has(Player player, String permission) { + return hasPermission(world(player), player(player), permission); + } + + @Override + public boolean playerHas(String world, String player, String permission) { + return hasPermission(world(world), player(player), permission); + } + + @Override + public boolean playerHas(World world, String player, String permission) { + return hasPermission(world(world), player(player), permission); + } + + @Override + public boolean playerHas(String world, OfflinePlayer player, String permission) { + return hasPermission(world(world), player(player), permission); + } + + @Override + public boolean playerHas(Player player, String permission) { + return hasPermission(world(player), player(player), permission); + } + + @Override + public boolean playerAdd(String world, String player, String permission) { + return playerAddPermission(world(world), player(player), permission); + } + + @Override + public boolean playerAdd(World world, String player, String permission) { + return playerAddPermission(world(world), player(player), permission); + } + + @Override + public boolean playerAdd(String world, OfflinePlayer player, String permission) { + return playerAddPermission(world(world), player(player), permission); + } + + @Override + public boolean playerAdd(Player player, String permission) { + return playerAddPermission(world(player), player(player), permission); + } + + @Override + public boolean playerRemove(String world, String player, String permission) { + return playerRemovePermission(world(world), player(player), permission); + } + + @Override + public boolean playerRemove(String world, OfflinePlayer player, String permission) { + return playerRemovePermission(world(world), player(player), permission); + } + + @Override + public boolean playerRemove(World world, String player, String permission) { + return playerRemovePermission(world(world), player(player), permission); + } + + @Override + public boolean playerRemove(Player player, String permission) { + return playerRemovePermission(world(player), player(player), permission); + } + + @Override + public boolean groupHas(String world, String group, String permission) { + return groupHasPermission(world(world), group, permission); + } + + @Override + public boolean groupHas(World world, String group, String permission) { + return groupHasPermission(world(world), group, permission); + } + + @Override + public boolean groupAdd(String world, String group, String permission) { + return groupAddPermission(world(world), group, permission); + } + + @Override + public boolean groupAdd(World world, String group, String permission) { + return groupAddPermission(world(world), group, permission); + } + + @Override + public boolean groupRemove(String world, String group, String permission) { + return groupRemovePermission(world(world), group, permission); + } + + @Override + public boolean groupRemove(World world, String group, String permission) { + return groupRemovePermission(world(world), group, permission); + } + + @Override + public boolean playerInGroup(String world, String player, String group) { + return playerInGroup(world(world), player(player), group); + } + + @Override + public boolean playerInGroup(World world, String player, String group) { + return playerInGroup(world(world), player(player), group); + } + + @Override + public boolean playerInGroup(String world, OfflinePlayer player, String group) { + return playerInGroup(world(world), player(player), group); + } + + @Override + public boolean playerInGroup(Player player, String group) { + return playerInGroup(world(player), player(player), group); + } + + @Override + public boolean playerAddGroup(String world, String player, String group) { + return playerAddGroup(world(world), player(player), group); + } + + @Override + public boolean playerAddGroup(World world, String player, String group) { + return playerAddGroup(world(world), player(player), group); + } + + @Override + public boolean playerAddGroup(String world, OfflinePlayer player, String group) { + return playerAddGroup(world(world), player(player), group); + } + + @Override + public boolean playerAddGroup(Player player, String group) { + return playerAddGroup(world(player), player(player), group); + } + + @Override + public boolean playerRemoveGroup(String world, String player, String group) { + return playerRemoveGroup(world(world), player(player), group); + } + + @Override + public boolean playerRemoveGroup(World world, String player, String group) { + return playerRemoveGroup(world(world), player(player), group); + } + + @Override + public boolean playerRemoveGroup(String world, OfflinePlayer player, String group) { + return playerRemoveGroup(world(world), player(player), group); + } + + @Override + public boolean playerRemoveGroup(Player player, String group) { + return playerRemoveGroup(world(player), player(player), group); + } + + @Override + public String[] getPlayerGroups(String world, String player) { + return playerGetGroups(world(world), player(player)); + } + + @Override + public String[] getPlayerGroups(World world, String player) { + return playerGetGroups(world(world), player(player)); + } + + @Override + public String[] getPlayerGroups(String world, OfflinePlayer player) { + return playerGetGroups(world(world), player(player)); + } + + @Override + public String[] getPlayerGroups(Player player) { + return playerGetGroups(world(player), player(player)); + } + + @Override + public String getPrimaryGroup(String world, String player) { + return playerPrimaryGroup(world(world), player(player)); + } + + @Override + public String getPrimaryGroup(World world, String player) { + return playerPrimaryGroup(world(world), player(player)); + } + + @Override + public String getPrimaryGroup(String world, OfflinePlayer player) { + return playerPrimaryGroup(world(world), player(player)); + } + + @Override + public String getPrimaryGroup(Player player) { + return playerPrimaryGroup(world(player), player(player)); + } + +} 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 8710e78bc..d5e80cd1a 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 @@ -25,13 +25,17 @@ package me.lucko.luckperms.bukkit.vault; -import lombok.NonNull; +import com.google.common.base.Preconditions; import me.lucko.luckperms.api.ChatMetaType; import me.lucko.luckperms.api.Contexts; import me.lucko.luckperms.api.Node; -import me.lucko.luckperms.api.caching.MetaData; +import me.lucko.luckperms.api.context.MutableContextSet; +import me.lucko.luckperms.bukkit.LPBukkitPlugin; import me.lucko.luckperms.common.caching.type.MetaAccumulator; +import me.lucko.luckperms.common.caching.type.MetaCache; +import me.lucko.luckperms.common.commands.CommandManager; +import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.model.Group; import me.lucko.luckperms.common.model.PermissionHolder; import me.lucko.luckperms.common.model.User; @@ -39,8 +43,7 @@ import me.lucko.luckperms.common.node.NodeFactory; import net.milkbowl.vault.chat.Chat; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; +import java.util.UUID; /** * An implementation of the Vault {@link Chat} API using LuckPerms. @@ -59,286 +62,268 @@ import org.bukkit.entity.Player; * We cannot risk blocking the main thread to load in their data. Again, this is due to crap Vault * design. There is nothing I can do about it. */ -public class VaultChatHook extends Chat { - private final VaultPermissionHook perms; +public class VaultChatHook extends AbstractVaultChat { - VaultChatHook(VaultPermissionHook perms) { - super(perms); - this.perms = perms; + // the plugin instance + private final LPBukkitPlugin plugin; + + // the vault permission implementation + private final VaultPermissionHook permissionHook; + + VaultChatHook(LPBukkitPlugin plugin, VaultPermissionHook permissionHook) { + super(permissionHook); + this.plugin = plugin; + this.permissionHook = permissionHook; + this.worldMappingFunction = world -> permissionHook.isIgnoreWorld() ? null : world; } public String getName() { - return perms.getName(); - } - - public boolean isEnabled() { - return perms.isEnabled(); - } - - private User getUser(String username) { - Player player = Bukkit.getPlayerExact(username); - return player == null ? null : perms.getPlugin().getUserManager().getIfLoaded(perms.getPlugin().getUuidCache().getUUID(player.getUniqueId())); + return "LuckPerms"; } @Override - public String getPlayerPrefix(String world, @NonNull String player) { - final User user = getUser(player); - return getHolderChatMeta(user, ChatMetaType.PREFIX, world); + public String getPlayerPrefix(String world, UUID uuid) { + Preconditions.checkNotNull(uuid, "uuid"); + User user = getUser(uuid); + if (user == null) { + return null; + } + Contexts contexts = permissionHook.contextForLookup(user, world); + MetaCache metaData = user.getCachedData().getMetaData(contexts); + String ret = metaData.getPrefix(); + if (log()) { + logMsg("#getPlayerPrefix: %s - %s - %s", user.getFriendlyName(), contexts.getContexts().toMultimap(), ret); + } + return ret; } @Override - public void setPlayerPrefix(String world, @NonNull String player, @NonNull String prefix) { - final User user = getUser(player); + public String getPlayerSuffix(String world, UUID uuid) { + Preconditions.checkNotNull(uuid, "uuid"); + User user = getUser(uuid); + if (user == null) { + return null; + } + Contexts contexts = permissionHook.contextForLookup(user, world); + MetaCache metaData = user.getCachedData().getMetaData(contexts); + String ret = metaData.getSuffix(); + if (log()) { + logMsg("#getPlayerSuffix: %s - %s - %s", user.getFriendlyName(), contexts.getContexts().toMultimap(), ret); + } + return ret; + } + + @Override + public void setPlayerPrefix(String world, UUID uuid, String prefix) { + Preconditions.checkNotNull(uuid, "uuid"); + User user = getUser(uuid); + if (user == null) { + return; + } setChatMeta(user, ChatMetaType.PREFIX, prefix, world); } @Override - public String getPlayerSuffix(String world, @NonNull String player) { - final User user = getUser(player); - return getHolderChatMeta(user, ChatMetaType.SUFFIX, world); - } - - @Override - public void setPlayerSuffix(String world, @NonNull String player, @NonNull String suffix) { - final User user = getUser(player); + public void setPlayerSuffix(String world, UUID uuid, String suffix) { + Preconditions.checkNotNull(uuid, "uuid"); + User user = getUser(uuid); + if (user == null) { + return; + } setChatMeta(user, ChatMetaType.SUFFIX, suffix, world); } @Override - public String getGroupPrefix(String world, @NonNull String group) { - final Group g = perms.getPlugin().getGroupManager().getByDisplayName(group); - return getHolderChatMeta(g, ChatMetaType.PREFIX, world); - } - - @Override - public void setGroupPrefix(String world, @NonNull String group, @NonNull String prefix) { - final Group g = perms.getPlugin().getGroupManager().getByDisplayName(group); - setChatMeta(g, ChatMetaType.PREFIX, prefix, world); - } - - @Override - public String getGroupSuffix(String world, @NonNull String group) { - final Group g = perms.getPlugin().getGroupManager().getByDisplayName(group); - return getHolderChatMeta(g, ChatMetaType.SUFFIX, world); - } - - @Override - public void setGroupSuffix(String world, @NonNull String group, @NonNull String suffix) { - final Group g = perms.getPlugin().getGroupManager().getByDisplayName(group); - setChatMeta(g, ChatMetaType.SUFFIX, suffix, world); - } - - @Override - public int getPlayerInfoInteger(String world, @NonNull String player, @NonNull String node, int defaultValue) { - final User user = getUser(player); - try { - return Integer.parseInt(getHolderMeta(user, node, world, String.valueOf(defaultValue))); - } catch (NumberFormatException e) { - return defaultValue; + public String getPlayerInfo(String world, UUID uuid, String key) { + Preconditions.checkNotNull(uuid, "uuid"); + Preconditions.checkNotNull(key, "key"); + User user = getUser(uuid); + if (user == null) { + return null; } - } - - @Override - public void setPlayerInfoInteger(String world, @NonNull String player, @NonNull String node, int value) { - final User user = getUser(player); - setMeta(user, node, String.valueOf(value), world); - } - - @Override - public int getGroupInfoInteger(String world, @NonNull String group, @NonNull String node, int defaultValue) { - final Group g = perms.getPlugin().getGroupManager().getByDisplayName(group); - try { - return Integer.parseInt(getHolderMeta(g, node, world, String.valueOf(defaultValue))); - } catch (NumberFormatException e) { - return defaultValue; + Contexts contexts = permissionHook.contextForLookup(user, world); + MetaCache metaData = user.getCachedData().getMetaData(contexts); + String ret = metaData.getMeta().get(key); + if (log()) { + logMsg("#getPlayerInfo: %s - %s - %s - %s", user.getFriendlyName(), contexts.getContexts().toMultimap(), key, ret); } + return ret; } @Override - public void setGroupInfoInteger(String world, @NonNull String group, @NonNull String node, int value) { - final Group g = perms.getPlugin().getGroupManager().getByDisplayName(group); - setMeta(g, node, String.valueOf(value), world); - } - - @Override - public double getPlayerInfoDouble(String world, @NonNull String player, @NonNull String node, double defaultValue) { - final User user = getUser(player); - try { - return Double.parseDouble(getHolderMeta(user, node, world, String.valueOf(defaultValue))); - } catch (NumberFormatException e) { - return defaultValue; - } - } - - @Override - public void setPlayerInfoDouble(String world, @NonNull String player, @NonNull String node, double value) { - final User user = getUser(player); - setMeta(user, node, String.valueOf(value), world); - } - - @Override - public double getGroupInfoDouble(String world, @NonNull String group, @NonNull String node, double defaultValue) { - final Group g = perms.getPlugin().getGroupManager().getByDisplayName(group); - try { - return Double.parseDouble(getHolderMeta(g, node, world, String.valueOf(defaultValue))); - } catch (NumberFormatException e) { - return defaultValue; - } - } - - @Override - public void setGroupInfoDouble(String world, @NonNull String group, @NonNull String node, double value) { - final Group g = perms.getPlugin().getGroupManager().getByDisplayName(group); - setMeta(g, node, String.valueOf(value), world); - } - - @Override - public boolean getPlayerInfoBoolean(String world, @NonNull String player, @NonNull String node, boolean defaultValue) { - final User user = getUser(player); - String s = getHolderMeta(user, node, world, String.valueOf(defaultValue)); - if (!s.equalsIgnoreCase("true") && !s.equalsIgnoreCase("false")) { - return defaultValue; - } - return Boolean.parseBoolean(s); - } - - @Override - public void setPlayerInfoBoolean(String world, @NonNull String player, @NonNull String node, boolean value) { - final User user = getUser(player); - setMeta(user, node, String.valueOf(value), world); - } - - @Override - public boolean getGroupInfoBoolean(String world, @NonNull String group, @NonNull String node, boolean defaultValue) { - final Group g = perms.getPlugin().getGroupManager().getByDisplayName(group); - String s = getHolderMeta(g, node, world, String.valueOf(defaultValue)); - if (!s.equalsIgnoreCase("true") && !s.equalsIgnoreCase("false")) { - return defaultValue; - } - return Boolean.parseBoolean(s); - } - - @Override - public void setGroupInfoBoolean(String world, @NonNull String group, @NonNull String node, boolean value) { - final Group g = perms.getPlugin().getGroupManager().getByDisplayName(group); - setMeta(g, node, String.valueOf(value), world); - } - - @Override - public String getPlayerInfoString(String world, @NonNull String player, @NonNull String node, String defaultValue) { - final User user = getUser(player); - return getHolderMeta(user, node, world, defaultValue); - } - - @Override - public void setPlayerInfoString(String world, @NonNull String player, @NonNull String node, String value) { - final User user = getUser(player); - setMeta(user, node, value, world); - } - - @Override - public String getGroupInfoString(String world, @NonNull String group, @NonNull String node, String defaultValue) { - final Group g = perms.getPlugin().getGroupManager().getByDisplayName(group); - return getHolderMeta(g, node, world, defaultValue); - } - - @Override - public void setGroupInfoString(String world, @NonNull String group, @NonNull String node, String value) { - final Group g = perms.getPlugin().getGroupManager().getByDisplayName(group); - setMeta(g, node, value, world); - } - - private void setMeta(PermissionHolder holder, String key, String value, String world) { - if (holder == null || key.isEmpty()) { + public void setPlayerInfo(String world, UUID uuid, String key, Object value) { + Preconditions.checkNotNull(uuid, "uuid"); + Preconditions.checkNotNull(key, "key"); + User user = getUser(uuid); + if (user == null) { return; } + setMeta(user, key, value, world); + } - String finalWorld = perms.correctWorld(world); - perms.log("Setting meta: '" + key + "' for " + holder.getObjectName() + " on world " + world + ", server " + perms.getVaultServer()); + @Override + public String getGroupsPrefix(String world, String name) { + Preconditions.checkNotNull(name, "name"); + Group group = getGroup(name); + if (group == null) { + return null; + } + Contexts contexts = permissionHook.contextForLookup(null, world); + MetaCache metaData = group.getCachedData().getMetaData(contexts); + String ret = metaData.getPrefix(); + if (log()) { + logMsg("#getGroupPrefix: %s - %s - %s", group.getName(), contexts.getContexts().toMultimap(), ret); + } + return ret; + } - perms.getExecutor().execute(() -> { - holder.removeIf(n -> n.isMeta() && n.getMeta().getKey().equals(key)); + @Override + public String getGroupsSuffix(String world, String name) { + Preconditions.checkNotNull(name, "name"); + Group group = getGroup(name); + if (group == null) { + return null; + } + Contexts contexts = permissionHook.contextForLookup(null, world); + MetaCache metaData = group.getCachedData().getMetaData(contexts); + String ret = metaData.getSuffix(); + if (log()) { + logMsg("#getGroupSuffix: %s - %s - %s", group.getName(), contexts.getContexts().toMultimap(), ret); + } + return ret; + } - Node.Builder metaNode; - if (key.equalsIgnoreCase(NodeFactory.PREFIX_KEY) || key.equalsIgnoreCase(NodeFactory.SUFFIX_KEY)) { - metaNode = NodeFactory.buildChatMetaNode(ChatMetaType.valueOf(key.toUpperCase()), 100, value); - } else { - metaNode = NodeFactory.buildMetaNode(key, value); - } + @Override + public void setGroupsPrefix(String world, String name, String prefix) { + Preconditions.checkNotNull(name, "name"); + Group group = getGroup(name); + if (group == null) { + return; + } + setChatMeta(group, ChatMetaType.PREFIX, prefix, world); + } - metaNode.setServer(perms.getVaultServer()); - metaNode.setWorld(finalWorld); + @Override + public void setGroupsSuffix(String world, String name, String suffix) { + Preconditions.checkNotNull(name, "name"); + Group group = getGroup(name); + if (group == null) { + return; + } + setChatMeta(group, ChatMetaType.SUFFIX, suffix, world); + } - holder.setPermission(metaNode.build()); - perms.holderSave(holder); - }); + @Override + public String getGroupInfo(String world, String name, String key) { + Preconditions.checkNotNull(name, "name"); + Preconditions.checkNotNull(key, "key"); + Group group = getGroup(name); + if (group == null) { + return null; + } + Contexts contexts = permissionHook.contextForLookup(null, world); + MetaCache metaData = group.getCachedData().getMetaData(contexts); + String ret = metaData.getMeta().get(key); + if (log()) { + logMsg("#getGroupInfo: %s - %s - %s - %s", group.getName(), contexts.getContexts().toMultimap(), key, ret); + } + return ret; + } + + @Override + public void setGroupInfo(String world, String name, String key, Object value) { + Preconditions.checkNotNull(name, "name"); + Preconditions.checkNotNull(key, "key"); + Group group = getGroup(name); + if (group == null) { + return; + } + setMeta(group, key, value, world); + } + + // utility methods for getting user and group instances + + private User getUser(UUID uuid) { + return plugin.getUserManager().getIfLoaded(plugin.getUuidCache().getUUID(uuid)); + } + + private Group getGroup(String name) { + return plugin.getGroupManager().getByDisplayName(name); + } + + // logging + private boolean log() { + return plugin.getConfiguration().get(ConfigKeys.VAULT_DEBUG); + } + private void logMsg(String format, Object... args) { + plugin.getLog().info("[VAULT-CHAT] " + String.format(format, args) + .replace(CommandManager.SECTION_CHAR, '$') + .replace(CommandManager.AMPERSAND_CHAR, '$') + ); } private void setChatMeta(PermissionHolder holder, ChatMetaType type, String value, String world) { - if (holder == null || value.equals("")) { - return; + if (log()) { + logMsg("#setChatMeta: %s - %s - %s - %s", holder.getFriendlyName(), type, value, world); } - String finalWorld = perms.correctWorld(world); - perms.log("Setting " + type.name().toLowerCase() + " for " + holder.getObjectName() + " on world " + world + ", server " + perms.getVaultServer()); - - perms.getExecutor().execute(() -> { + permissionHook.getExecutor().execute(() -> { // remove all prefixes/suffixes directly set on the user/group holder.removeIf(type::matches); + if (value == null) { + permissionHook.holderSave(holder); + return; + } + // find the max inherited priority & add 10 - MetaAccumulator metaAccumulator = holder.accumulateMeta(null, null, perms.createContextForWorldSet(finalWorld)); + MetaAccumulator metaAccumulator = holder.accumulateMeta(null, null, createContextForWorldSet(world)); int priority = (type == ChatMetaType.PREFIX ? metaAccumulator.getPrefixes() : metaAccumulator.getSuffixes()).keySet().stream() .mapToInt(e -> e).max().orElse(0) + 10; Node.Builder chatMetaNode = NodeFactory.buildChatMetaNode(type, priority, value); - chatMetaNode.setServer(perms.getVaultServer()); - chatMetaNode.setWorld(finalWorld); + chatMetaNode.setServer(permissionHook.getVaultServer()); + chatMetaNode.setWorld(world); holder.setPermission(chatMetaNode.build()); - perms.holderSave(holder); + permissionHook.holderSave(holder); }); } - private String getHolderMeta(PermissionHolder holder, String node, String world, String defaultValue) { - if (holder == null) { - return defaultValue; + private void setMeta(PermissionHolder holder, String key, Object value, String world) { + if (log()) { + logMsg("#setMeta: %s - %s - %s - %s", holder.getFriendlyName(), key, value, world); } - world = perms.correctWorld(world); + permissionHook.getExecutor().execute(() -> { + holder.removeIf(n -> n.isMeta() && n.getMeta().getKey().equals(key)); - Contexts contexts; - if (holder.getType().isUser()) { - contexts = perms.createContextForWorldLookup(perms.getPlugin().getPlayer((User) holder), world); - } else { - contexts = perms.createContextForWorldLookup(world); - } + if (value == null) { + permissionHook.holderSave(holder); + return; + } - perms.log("Getting meta: '" + node + "' for holder " + holder.getFriendlyName() + " in contexts " + contexts); + Node.Builder metaNode; + if (key.equalsIgnoreCase(NodeFactory.PREFIX_KEY) || key.equalsIgnoreCase(NodeFactory.SUFFIX_KEY)) { + metaNode = NodeFactory.buildChatMetaNode(ChatMetaType.valueOf(key.toUpperCase()), 100, value.toString()); + } else { + metaNode = NodeFactory.buildMetaNode(key, value.toString()); + } - String ret = holder.getCachedData().getMetaData(contexts).getMeta().get(node); - return ret != null ? ret : defaultValue; + metaNode.setServer(permissionHook.getVaultServer()); + metaNode.setWorld(world); + + holder.setPermission(metaNode.build()); + permissionHook.holderSave(holder); + }); } - private String getHolderChatMeta(PermissionHolder holder, ChatMetaType type, String world) { - if (holder == null) { - return ""; + private Contexts createContextForWorldSet(String world) { + MutableContextSet context = MutableContextSet.create(); + if (world != null && !world.equals("") && !world.equalsIgnoreCase("global")) { + context.add(Contexts.WORLD_KEY, world.toLowerCase()); } - - world = perms.correctWorld(world); - - Contexts contexts; - if (holder.getType().isUser()) { - contexts = perms.createContextForWorldLookup(perms.getPlugin().getPlayer((User) holder), world); - } else { - contexts = perms.createContextForWorldLookup(world); - } - - perms.log("Getting " + type.name().toLowerCase() + " for holder " + holder.getFriendlyName() + " in contexts " + contexts); - - MetaData data = holder.getCachedData().getMetaData(contexts); - String ret = type == ChatMetaType.PREFIX ? data.getPrefix() : data.getSuffix(); - return ret != null ? ret : ""; + context.add(Contexts.SERVER_KEY, permissionHook.getVaultServer()); + return new Contexts(context, permissionHook.isIncludeGlobal(), true, true, true, true, false); } - } diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/VaultExecutor.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/VaultExecutor.java deleted file mode 100644 index d49436cb6..000000000 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/VaultExecutor.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * This file is part of LuckPerms, licensed under the MIT License. - * - * Copyright (c) lucko (Luck) - * Copyright (c) contributors - * - * 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.bukkit.vault; - -import me.lucko.luckperms.bukkit.LPBukkitPlugin; - -import org.bukkit.scheduler.BukkitTask; - -import java.util.Queue; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.Executor; - -/** - * Sequential executor for Vault modifications - */ -public class VaultExecutor implements Runnable, Executor { - - private BukkitTask task = null; - private final Queue tasks = new ConcurrentLinkedQueue<>(); - - public VaultExecutor(LPBukkitPlugin plugin) { - task = plugin.getServer().getScheduler().runTaskTimerAsynchronously(plugin, this, 1L, 1L); - } - - @Override - public void execute(Runnable r) { - tasks.offer(r); - } - - @Override - public void run() { - for (Runnable runnable; (runnable = tasks.poll()) != null; ) { - try { - runnable.run(); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - - public void close() { - if (task != null) { - task.cancel(); - task = null; - } - } -} diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/VaultHookManager.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/VaultHookManager.java index 68ba65761..d5ca3dede 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/VaultHookManager.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/VaultHookManager.java @@ -56,7 +56,7 @@ public class VaultHookManager { } if (chatHook == null) { - chatHook = new VaultChatHook(permissionHook); + chatHook = new VaultChatHook(plugin, permissionHook); } final ServicesManager sm = plugin.getServer().getServicesManager(); 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 3f14f4e12..7c980fef8 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 @@ -26,15 +26,17 @@ package me.lucko.luckperms.bukkit.vault; import lombok.Getter; -import lombok.NonNull; import com.google.common.base.Preconditions; import me.lucko.luckperms.api.Contexts; import me.lucko.luckperms.api.Node; +import me.lucko.luckperms.api.Tristate; import me.lucko.luckperms.api.context.ContextSet; import me.lucko.luckperms.api.context.MutableContextSet; import me.lucko.luckperms.bukkit.LPBukkitPlugin; +import me.lucko.luckperms.common.caching.type.PermissionCache; +import me.lucko.luckperms.common.commands.CommandManager; import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.model.Group; import me.lucko.luckperms.common.model.PermissionHolder; @@ -44,12 +46,12 @@ import me.lucko.luckperms.common.verbose.CheckOrigin; import net.milkbowl.vault.permission.Permission; -import org.bukkit.Bukkit; -import org.bukkit.OfflinePlayer; -import org.bukkit.World; -import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -import org.bukkit.plugin.java.JavaPlugin; + +import java.util.Arrays; +import java.util.UUID; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; /** * An implementation of the Vault {@link Permission} API using LuckPerms. @@ -69,18 +71,18 @@ import org.bukkit.plugin.java.JavaPlugin; * design. There is nothing I can do about it. */ @Getter -public class VaultPermissionHook extends Permission { +public class VaultPermissionHook extends AbstractVaultPermission { // the plugin instance - private LPBukkitPlugin plugin; + private final LPBukkitPlugin plugin; // an executor for Vault modifications. - private VaultExecutor executor; + private final Executor executor; public VaultPermissionHook(LPBukkitPlugin plugin) { this.plugin = plugin; - super.plugin = JavaPlugin.getProvidingPlugin(Permission.class); - this.executor = new VaultExecutor(plugin); + this.executor = Executors.newSingleThreadExecutor(); + this.worldMappingFunction = world -> isIgnoreWorld() ? null : world; } @Override @@ -88,90 +90,51 @@ public class VaultPermissionHook extends Permission { return "LuckPerms"; } + // override this check to delegate to Player#hasPermission @Override - public boolean isEnabled() { - return plugin.isEnabled(); + public boolean has(Player player, String permission) { + return player.hasPermission(permission); } + // override this check to delegate to Player#hasPermission @Override - public boolean hasSuperPermsCompat() { - return true; - } - - @Override - public boolean hasGroupSupport() { - return true; - } - - @Override - public String[] getGroups() { - return plugin.getGroupManager().getAll().values().stream().map(g -> g.getDisplayName().orElse(g.getName())).toArray(String[]::new); - } - - @Override - public boolean has(@NonNull CommandSender sender, @NonNull String permission) { - return sender.hasPermission(permission); - } - - @Override - public boolean has(@NonNull Player player, @NonNull String permission) { + public boolean playerHas(Player player, String permission) { return player.hasPermission(permission); } @Override - public boolean playerHas(String world, @NonNull String player, @NonNull String permission) { - return playerHas(world, Bukkit.getPlayerExact(player), permission); + public String[] getGroups() { + return plugin.getGroupManager().getAll().values().stream() + .map(g -> g.getDisplayName().orElse(g.getName())) + .toArray(String[]::new); } @Override - public boolean playerHas(String world, @NonNull OfflinePlayer player, @NonNull String permission) { - return playerHas(world, player.getPlayer(), permission); - } + public boolean hasPermission(String world, UUID uuid, String permission) { + Preconditions.checkNotNull(uuid, "uuid"); + Preconditions.checkNotNull(permission, "permission"); - private boolean playerHas(String world, Player player, String permission) { - world = correctWorld(world); - - if (player == null) { - return false; - } - - User user = plugin.getUserManager().getIfLoaded(plugin.getUuidCache().getUUID(player.getUniqueId())); + User user = getUser(uuid); if (user == null) { return false; } - Contexts contexts = createContextForWorldLookup(player, world); - log("Checking if player " + player + " has permission: " + permission + " in contexts " + contexts); + Contexts contexts = contextForLookup(user, world); + PermissionCache permissionData = user.getCachedData().getPermissionData(contexts); - // Effectively fallback to the standard Bukkit #hasPermission check. - return user.getCachedData().getPermissionData(contexts).getPermissionValue(permission, CheckOrigin.INTERNAL).asBoolean(); - } - - @Override - public boolean playerAdd(String world, @NonNull String player, @NonNull String permission) { - return playerAdd(world, Bukkit.getPlayerExact(player), permission); - } - - @SuppressWarnings("deprecation") - @Override - public boolean playerAdd(World world, @NonNull String player, @NonNull String permission) { - return playerAdd(world == null ? null : world.getName(), Bukkit.getPlayerExact(player), permission); - } - - @Override - public boolean playerAdd(String world, @NonNull OfflinePlayer player, @NonNull String permission) { - return playerAdd(world, player.getPlayer(), permission); - } - - private boolean playerAdd(String world, Player player, String permission) { - world = correctWorld(world); - log("Adding permission to player " + player + ": '" + permission + "' on world " + world + ", server " + getVaultServer()); - - if (player == null) { - return false; + Tristate result = permissionData.getPermissionValue(permission, CheckOrigin.INTERNAL); + if (log()) { + logMsg("#hasPermission: %s - %s - %s - %s", user.getFriendlyName(), contexts.getContexts().toMultimap(), permission, result); } + return result.asBoolean(); + } - final User user = plugin.getUserManager().getIfLoaded(plugin.getUuidCache().getUUID(player.getUniqueId())); + @Override + public boolean playerAddPermission(String world, UUID uuid, String permission) { + Preconditions.checkNotNull(uuid, "uuid"); + Preconditions.checkNotNull(permission, "permission"); + + User user = getUser(uuid); if (user == null) { return false; } @@ -181,30 +144,11 @@ public class VaultPermissionHook extends Permission { } @Override - public boolean playerRemove(String world, @NonNull String player, @NonNull String permission) { - return playerRemove(world, Bukkit.getPlayerExact(player), permission); - } + public boolean playerRemovePermission(String world, UUID uuid, String permission) { + Preconditions.checkNotNull(uuid, "uuid"); + Preconditions.checkNotNull(permission, "permission"); - @Override - public boolean playerRemove(String world, @NonNull OfflinePlayer player, @NonNull String permission) { - return playerRemove(world, player.getPlayer(), permission); - } - - @SuppressWarnings("deprecation") - @Override - public boolean playerRemove(World world, @NonNull String player, @NonNull String permission) { - return playerRemove(world == null ? null : world.getName(), Bukkit.getPlayerExact(player), permission); - } - - private boolean playerRemove(String world, Player player, String permission) { - world = correctWorld(world); - log("Removing permission from player " + player + ": '" + permission + "' on world " + world + ", server " + getVaultServer()); - - if (player == null) { - return false; - } - - final User user = plugin.getUserManager().getIfLoaded(plugin.getUuidCache().getUUID(player.getUniqueId())); + User user = getUser(uuid); if (user == null) { return false; } @@ -214,64 +158,134 @@ public class VaultPermissionHook extends Permission { } @Override - public boolean groupHas(String world, @NonNull String groupName, @NonNull String permission) { - world = correctWorld(world); + public boolean playerInGroup(String world, UUID uuid, String group) { + Preconditions.checkNotNull(uuid, "uuid"); + Preconditions.checkNotNull(group, "group"); + return hasPermission(world, uuid, NodeFactory.groupNode(rewriteGroupName(group))); + } - final Group group = plugin.getGroupManager().getByDisplayName(groupName); + @Override + public boolean playerAddGroup(String world, UUID uuid, String group) { + Preconditions.checkNotNull(uuid, "uuid"); + Preconditions.checkNotNull(group, "group"); + return checkGroupExists(group) && playerAddPermission(world, uuid, NodeFactory.groupNode(rewriteGroupName(group))); + } + + @Override + public boolean playerRemoveGroup(String world, UUID uuid, String group) { + Preconditions.checkNotNull(uuid, "uuid"); + Preconditions.checkNotNull(group, "group"); + return checkGroupExists(group) && playerRemovePermission(world, uuid, NodeFactory.groupNode(rewriteGroupName(group))); + } + + @Override + public String[] playerGetGroups(String world, UUID uuid) { + Preconditions.checkNotNull(uuid, "uuid"); + + User user = getUser(uuid); + if (user == null) { + return new String[0]; + } + + ContextSet contexts = contextForLookup(user, world).getContexts(); + + String[] ret = user.getEnduringNodes().values().stream() + .filter(Node::isGroupNode) + .filter(n -> n.shouldApplyWithContext(contexts)) + .map(n -> { + Group group = plugin.getGroupManager().getIfLoaded(n.getGroupName()); + if (group != null) { + return group.getDisplayName().orElse(group.getName()); + } + return n.getGroupName(); + }) + .toArray(String[]::new); + + if (log()) { + logMsg("#playerGetGroups: %s - %s - %s", user.getFriendlyName(), contexts, Arrays.toString(ret)); + } + + return ret; + } + + @Override + public String playerPrimaryGroup(String world, UUID uuid) { + Preconditions.checkNotNull(uuid, "uuid"); + + User user = getUser(uuid); + if (user == null) { + return null; + } + + String value = user.getPrimaryGroup().getValue(); + Group group = getGroup(value); + if (group != null) { + return group.getDisplayName().orElse(group.getName()); + } + + if (log()) { + logMsg("#playerPrimaryGroup: %s - %s - %s", user.getFriendlyName(), world, value); + } + + return value; + } + + @Override + public boolean groupHasPermission(String world, String name, String permission) { + Preconditions.checkNotNull(name, "name"); + Preconditions.checkNotNull(permission, "permission"); + + Group group = getGroup(name); if (group == null) { return false; } - Contexts contexts = createContextForWorldLookup(world); - log("Checking if group " + groupName + " has permission: " + permission + " in contexts " + contexts); + Contexts contexts = contextForLookup(null, world); + PermissionCache permissionData = group.getCachedData().getPermissionData(contexts); - // Effectively fallback to the standard Bukkit #hasPermission check. - return group.getCachedData().getPermissionData(createContextForWorldLookup(world)).getPermissionValue(permission, CheckOrigin.INTERNAL).asBoolean(); + Tristate result = permissionData.getPermissionValue(permission, CheckOrigin.INTERNAL); + if (log()) { + logMsg("#groupHasPermission: %s - %s - %s - %s", group.getName(), contexts.getContexts().toMultimap(), permission, result); + } + return result.asBoolean(); } @Override - public boolean groupAdd(String world, @NonNull String groupName, @NonNull String permission) { - world = correctWorld(world); - log("Adding permission to group " + groupName + ": '" + permission + "' on world " + world + ", server " + getVaultServer()); + public boolean groupAddPermission(String world, String name, String permission) { + Preconditions.checkNotNull(name, "name"); + Preconditions.checkNotNull(permission, "permission"); - final Group group = plugin.getGroupManager().getByDisplayName(groupName); - if (group == null) return false; + Group group = getGroup(name); + if (group == null) { + return false; + } holderAddPermission(group, permission, world); return true; } @Override - public boolean groupRemove(String world, @NonNull String groupName, @NonNull String permission) { - world = correctWorld(world); - log("Removing permission from group " + groupName + ": '" + permission + "' on world " + world + ", server " + getVaultServer()); + public boolean groupRemovePermission(String world, String name, String permission) { + Preconditions.checkNotNull(name, "name"); + Preconditions.checkNotNull(permission, "permission"); - final Group group = plugin.getGroupManager().getByDisplayName(groupName); - if (group == null) return false; + Group group = getGroup(name); + if (group == null) { + return false; + } holderRemovePermission(group, permission, world); return true; } - @Override - public boolean playerInGroup(String world, String player, @NonNull String group) { - return playerHas(world, player, NodeFactory.groupNode(rewriteGroupName(group))); + // utility methods for getting user and group instances + + private User getUser(UUID uuid) { + return plugin.getUserManager().getIfLoaded(plugin.getUuidCache().getUUID(uuid)); } - @SuppressWarnings("deprecation") - @Override - public boolean playerInGroup(World world, String player, @NonNull String group) { - return playerHas(world, player, NodeFactory.groupNode(rewriteGroupName(group))); - } - - @Override - public boolean playerInGroup(String world, OfflinePlayer player, @NonNull String group) { - return playerHas(world, player, NodeFactory.groupNode(rewriteGroupName(group))); - } - - @Override - public boolean playerInGroup(Player player, @NonNull String group) { - return playerHas(player, NodeFactory.groupNode(rewriteGroupName(group))); + private Group getGroup(String name) { + return plugin.getGroupManager().getByDisplayName(name); } private boolean checkGroupExists(String group) { @@ -286,139 +300,49 @@ public class VaultPermissionHook extends Permission { return name; } - @Override - public boolean playerAddGroup(String world, String player, @NonNull String group) { - return checkGroupExists(group) && playerAdd(world, player, NodeFactory.groupNode(rewriteGroupName(group))); + // logging + private boolean log() { + return plugin.getConfiguration().get(ConfigKeys.VAULT_DEBUG); + } + private void logMsg(String format, Object... args) { + plugin.getLog().info("[VAULT-PERMS] " + String.format(format, args) + .replace(CommandManager.SECTION_CHAR, '$') + .replace(CommandManager.AMPERSAND_CHAR, '$') + ); } - @SuppressWarnings("deprecation") - @Override - public boolean playerAddGroup(World world, String player, @NonNull String group) { - return checkGroupExists(group) && playerAdd(world, player, NodeFactory.groupNode(rewriteGroupName(group))); - } + // utility method for getting a contexts instance for a given vault lookup. + Contexts contextForLookup(User user, String world) { + MutableContextSet context; - @Override - public boolean playerAddGroup(String world, OfflinePlayer player, @NonNull String group) { - return checkGroupExists(group) && playerAdd(world, player, NodeFactory.groupNode(rewriteGroupName(group))); - } - - @Override - public boolean playerAddGroup(Player player, @NonNull String group) { - return checkGroupExists(group) && playerAdd(player, NodeFactory.groupNode(rewriteGroupName(group))); - } - - @Override - public boolean playerRemoveGroup(String world, String player, @NonNull String group) { - return checkGroupExists(group) && playerRemove(world, player, NodeFactory.groupNode(rewriteGroupName(group))); - } - - @SuppressWarnings("deprecation") - @Override - public boolean playerRemoveGroup(World world, String player, @NonNull String group) { - return checkGroupExists(group) && playerRemove(world, player, NodeFactory.groupNode(rewriteGroupName(group))); - } - - @Override - public boolean playerRemoveGroup(String world, OfflinePlayer player, @NonNull String group) { - return checkGroupExists(group) && playerRemove(world, player, NodeFactory.groupNode(rewriteGroupName(group))); - } - - @Override - public boolean playerRemoveGroup(Player player, @NonNull String group) { - return checkGroupExists(group) && playerRemove(player, NodeFactory.groupNode(rewriteGroupName(group))); - } - - @Override - public String[] getPlayerGroups(String world, @NonNull String player) { - return getPlayerGroups(world, Bukkit.getPlayerExact(player)); - } - - @SuppressWarnings("deprecation") - @Override - public String[] getPlayerGroups(World world, @NonNull String player) { - return getPlayerGroups(world == null ? null : world.getName(), Bukkit.getPlayerExact(player)); - } - - @Override - public String[] getPlayerGroups(String world, @NonNull OfflinePlayer player) { - return getPlayerGroups(world, player.getPlayer()); - } - - private String[] getPlayerGroups(String world, Player player) { - world = correctWorld(world); - - if (player == null) { - return new String[0]; + Player player = user == null ? null : plugin.getPlayer(user); + if (player != null) { + context = plugin.getContextManager().getApplicableContext(player).mutableCopy(); + } else { + context = plugin.getContextManager().getStaticContext().mutableCopy(); } - User user = plugin.getUserManager().getIfLoaded(plugin.getUuidCache().getUUID(player.getUniqueId())); - if (user == null) { - return new String[0]; + // if world is null, we want to do a lookup in the players current context + // if world is not null, we want to do a lookup in that specific world + if (world != null && !world.isEmpty()) { + // remove already accumulated worlds + context.removeAll(Contexts.WORLD_KEY); + // add the vault world + context.add(Contexts.WORLD_KEY, world.toLowerCase()); } - ContextSet contexts = createContextForWorldLookup(player, world).getContexts(); - log("Getting groups of player: " + player + " in contexts " + contexts); + // if we're using a special vault server + if (useVaultServer()) { + // remove the normal server context from the set + context.remove(Contexts.SERVER_KEY, getServer()); - return user.getEnduringNodes().values().stream() - .filter(Node::isGroupNode) - .filter(n -> n.shouldApplyWithContext(contexts)) - .map(n -> { - Group group = plugin.getGroupManager().getIfLoaded(n.getGroupName()); - if (group != null) { - return group.getDisplayName().orElse(group.getName()); - } - return n.getGroupName(); - }) - .toArray(String[]::new); - } - - @Override - public String getPrimaryGroup(String world, @NonNull String player) { - return getPrimaryGroup(Bukkit.getPlayerExact(player)); - } - - @SuppressWarnings("deprecation") - @Override - public String getPrimaryGroup(World world, @NonNull String player) { - return getPrimaryGroup(Bukkit.getPlayerExact(player)); - } - - @Override - public String getPrimaryGroup(String world, @NonNull OfflinePlayer player) { - return getPrimaryGroup(player.getPlayer()); - } - - @Override - public String getPrimaryGroup(Player player) { - log("Getting primary group of player: " + player); - - if (player == null) { - return null; + // add the vault specific server + if (!getVaultServer().equals("global")) { + context.add(Contexts.SERVER_KEY, getVaultServer()); + } } - final User user = plugin.getUserManager().getIfLoaded(plugin.getUuidCache().getUUID(player.getUniqueId())); - - if (user == null) { - return null; - } - - String g = user.getPrimaryGroup().getValue(); - Group group = plugin.getGroupManager().getByDisplayName(g); - if (group != null) { - return group.getDisplayName().orElse(group.getName()); - } - - return g; - } - - public void log(String s) { - if (plugin.getConfiguration().get(ConfigKeys.VAULT_DEBUG)) { - plugin.getLog().info("[VAULT] " + s); - } - } - - String correctWorld(String world) { - return isIgnoreWorld() ? null : world; + return new Contexts(context, isIncludeGlobal(), true, true, true, true, false); } // utility methods for modifying the state of PermissionHolders @@ -426,6 +350,11 @@ public class VaultPermissionHook extends Permission { private void holderAddPermission(PermissionHolder holder, String permission, String world) { Preconditions.checkNotNull(permission, "permission is null"); Preconditions.checkArgument(!permission.isEmpty(), "permission is an empty string"); + + if (log()) { + logMsg("#holderAddPermission: %s - %s - %s", holder.getFriendlyName(), permission, world); + } + executor.execute(() -> { if (holder.setPermission(NodeFactory.make(permission, true, getVaultServer(), world)).asBoolean()) { holderSave(holder); @@ -436,6 +365,11 @@ public class VaultPermissionHook extends Permission { private void holderRemovePermission(PermissionHolder holder, String permission, String world) { Preconditions.checkNotNull(permission, "permission is null"); Preconditions.checkArgument(!permission.isEmpty(), "permission is an empty string"); + + if (log()) { + logMsg("#holderRemovePermission: %s - %s - %s", holder.getFriendlyName(), permission, world); + } + executor.execute(() -> { if (holder.unsetPermission(NodeFactory.make(permission, getVaultServer(), world)).asBoolean()) { holderSave(holder); @@ -454,63 +388,6 @@ public class VaultPermissionHook extends Permission { } } - // helper methods to build Contexts instances for different world/server combinations - - public Contexts createContextForWorldSet(String world) { - MutableContextSet context = MutableContextSet.create(); - if (world != null && !world.equals("") && !world.equalsIgnoreCase("global")) { - context.add(Contexts.WORLD_KEY, world.toLowerCase()); - } - context.add(Contexts.SERVER_KEY, getVaultServer()); - return new Contexts(context, isIncludeGlobal(), true, true, true, true, false); - } - - public Contexts createContextForWorldLookup(String world) { - MutableContextSet context = plugin.getContextManager().getStaticContext().mutableCopy(); - - if (useVaultServer()) { - // remove already accumulated worlds - context.removeAll(Contexts.WORLD_KEY); - // add the vault world - if (world != null && !world.isEmpty() && !world.equalsIgnoreCase("global")) { - context.add(Contexts.WORLD_KEY, world.toLowerCase()); - } - - // remove the server context from global - context.remove(Contexts.SERVER_KEY, getServer()); - - // add the vault specific server - if (!getVaultServer().equals("global")) { - context.add(Contexts.SERVER_KEY, getVaultServer()); - } - } - - return new Contexts(context, isIncludeGlobal(), true, true, true, true, false); - } - - public Contexts createContextForWorldLookup(@NonNull Player player, String world) { - MutableContextSet context = plugin.getContextManager().getApplicableContext(player).mutableCopy(); - - if (useVaultServer()) { - // remove already accumulated worlds - context.removeAll(Contexts.WORLD_KEY); - // add the vault world - if (world != null && !world.isEmpty() && !world.equalsIgnoreCase("global")) { - context.add(Contexts.WORLD_KEY, world.toLowerCase()); - } - - // remove the server context from global - context.remove(Contexts.SERVER_KEY, getServer()); - - // add the vault specific server - if (!getVaultServer().equals("global")) { - context.add(Contexts.SERVER_KEY, getVaultServer()); - } - } - - return new Contexts(context, isIncludeGlobal(), true, true, true, true, false); - } - // helper methods to just pull values from the config. String getServer() { @@ -521,10 +398,6 @@ public class VaultPermissionHook extends Permission { return plugin.getConfiguration().get(ConfigKeys.VAULT_SERVER); } - boolean useVaultServer() { - return plugin.getConfiguration().get(ConfigKeys.USE_VAULT_SERVER); - } - boolean isIncludeGlobal() { return plugin.getConfiguration().get(ConfigKeys.VAULT_INCLUDING_GLOBAL); } @@ -532,4 +405,8 @@ public class VaultPermissionHook extends Permission { boolean isIgnoreWorld() { return plugin.getConfiguration().get(ConfigKeys.VAULT_IGNORE_WORLD); } + + private boolean useVaultServer() { + return plugin.getConfiguration().get(ConfigKeys.USE_VAULT_SERVER); + } }