diff --git a/src/main/java/fr/xephi/authme/OfflinePlayerWrapper.java b/src/main/java/fr/xephi/authme/OfflinePlayerInfo.java similarity index 52% rename from src/main/java/fr/xephi/authme/OfflinePlayerWrapper.java rename to src/main/java/fr/xephi/authme/OfflinePlayerInfo.java index 13431474c..cdfcb1a78 100644 --- a/src/main/java/fr/xephi/authme/OfflinePlayerWrapper.java +++ b/src/main/java/fr/xephi/authme/OfflinePlayerInfo.java @@ -1,22 +1,24 @@ package fr.xephi.authme; +import java.util.Optional; import java.util.UUID; -public class OfflinePlayerWrapper { +public class OfflinePlayerInfo { - private final UUID uniqueId; private final String name; + private final Optional uniqueId; - public OfflinePlayerWrapper(UUID uniqueId, String name) { - this.uniqueId = uniqueId; + public OfflinePlayerInfo(String name, Optional uniqueId) { this.name = name; - } - - public UUID getUniqueId() { - return uniqueId; + this.uniqueId = uniqueId; } public String getName() { return name; } + + public Optional getUniqueId() { + return uniqueId; + } + } diff --git a/src/main/java/fr/xephi/authme/listener/JoiningPlayer.java b/src/main/java/fr/xephi/authme/listener/JoiningPlayer.java index 057694aa9..8831cac16 100644 --- a/src/main/java/fr/xephi/authme/listener/JoiningPlayer.java +++ b/src/main/java/fr/xephi/authme/listener/JoiningPlayer.java @@ -1,9 +1,8 @@ package fr.xephi.authme.listener; -import fr.xephi.authme.OfflinePlayerWrapper; +import fr.xephi.authme.OfflinePlayerInfo; import fr.xephi.authme.permission.PermissionNode; import fr.xephi.authme.permission.PermissionsManager; -import org.bukkit.OfflinePlayer; import org.bukkit.entity.Player; import java.util.concurrent.CompletableFuture; @@ -34,11 +33,12 @@ public final class JoiningPlayer { /** * Creates a {@link JoiningPlayer} instance from the given name. * - * @param player the offline player + * @param offline the offline player information * @return the created instance */ - public static JoiningPlayer fromOfflinePlayer(OfflinePlayerWrapper player) { - return new JoiningPlayer(player.getName(), (manager, perm) -> manager.hasPermissionOffline(player, perm)); + public static JoiningPlayer fromOfflinePlayerInfo(OfflinePlayerInfo offline) { + return new JoiningPlayer(offline.getName(), (manager, perm) -> + manager.hasPermissionOffline(offline, perm)); } /** diff --git a/src/main/java/fr/xephi/authme/permission/handlers/BPermissionsHandler.java b/src/main/java/fr/xephi/authme/permission/handlers/BPermissionsHandler.java index 88f8a0aa8..aee4a97bf 100644 --- a/src/main/java/fr/xephi/authme/permission/handlers/BPermissionsHandler.java +++ b/src/main/java/fr/xephi/authme/permission/handlers/BPermissionsHandler.java @@ -2,9 +2,10 @@ package fr.xephi.authme.permission.handlers; import de.bananaco.bpermissions.api.ApiLayer; import de.bananaco.bpermissions.api.CalculableType; -import fr.xephi.authme.OfflinePlayerWrapper; +import fr.xephi.authme.OfflinePlayerInfo; import fr.xephi.authme.permission.PermissionNode; import fr.xephi.authme.permission.PermissionsSystemType; +import org.bukkit.entity.Player; import java.util.Arrays; import java.util.List; @@ -21,9 +22,8 @@ import static java.util.concurrent.CompletableFuture.completedFuture; public class BPermissionsHandler implements PermissionHandler { @Override - public CompletableFuture addToGroup(OfflinePlayerWrapper player, String groupName) { - ApiLayer.addGroup(null, CalculableType.USER, player.getName(), groupName); - return completedFuture(true); + public PermissionsSystemType getPermissionSystem() { + return PermissionsSystemType.B_PERMISSIONS; } @Override @@ -32,34 +32,64 @@ public class BPermissionsHandler implements PermissionHandler { } @Override - public CompletableFuture hasPermissionOffline(OfflinePlayerWrapper player, PermissionNode node) { - return completedFuture(ApiLayer.hasPermission(null, CalculableType.USER, player.getName(), node.getNode())); + public CompletableFuture hasPermissionOffline(OfflinePlayerInfo offlineInfo, PermissionNode node) { + return completedFuture(ApiLayer.hasPermission(null, CalculableType.USER, offlineInfo.getName(), node.getNode())); } @Override - public CompletableFuture isInGroup(OfflinePlayerWrapper player, String groupName) { - return completedFuture(ApiLayer.hasGroup(null, CalculableType.USER, player.getName(), groupName)); + public List getGroups(Player player) { + return Arrays.asList(ApiLayer.getGroups(null, CalculableType.USER, player.getName())); } @Override - public CompletableFuture removeFromGroup(OfflinePlayerWrapper player, String groupName) { + public CompletableFuture> getGroupsOffline(OfflinePlayerInfo offlineInfo) { + return completedFuture(Arrays.asList(ApiLayer.getGroups(null, CalculableType.USER, offlineInfo.getName()))); + } + + @Override + public boolean isInGroup(Player player, String groupName) { + return ApiLayer.hasGroup(null, CalculableType.USER, player.getName(), groupName); + } + + @Override + public CompletableFuture isInGroupOffline(OfflinePlayerInfo offlineInfo, String groupName) { + return completedFuture(ApiLayer.hasGroup(null, CalculableType.USER, offlineInfo.getName(), groupName)); + } + + @Override + public boolean addToGroup(Player player, String groupName) { + ApiLayer.addGroup(null, CalculableType.USER, player.getName(), groupName); + return true; + } + + @Override + public CompletableFuture addToGroupOffline(OfflinePlayerInfo offlineInfo, String groupName) { + ApiLayer.addGroup(null, CalculableType.USER, offlineInfo.getName(), groupName); + return completedFuture(true); + } + + @Override + public boolean removeFromGroup(Player player, String groupName) { ApiLayer.removeGroup(null, CalculableType.USER, player.getName(), groupName); + return true; + } + + @Override + public CompletableFuture removeFromGroupOffline(OfflinePlayerInfo playerInfo, String groupName) { + ApiLayer.removeGroup(null, CalculableType.USER, playerInfo.getName(), groupName); return completedFuture(true); } @Override - public CompletableFuture setGroup(OfflinePlayerWrapper player, String group) { + public boolean setGroup(Player player, String group) { ApiLayer.setGroup(null, CalculableType.USER, player.getName(), group); + return true; + } + + @Override + public CompletableFuture setGroupOffline(OfflinePlayerInfo offlineInfo, String group) { + ApiLayer.setGroup(null, CalculableType.USER, offlineInfo.getName(), group); return completedFuture(true); } - @Override - public CompletableFuture> getGroups(OfflinePlayerWrapper player) { - return completedFuture(Arrays.asList(ApiLayer.getGroups(null, CalculableType.USER, player.getName()))); - } - - @Override - public PermissionsSystemType getPermissionSystem() { - return PermissionsSystemType.B_PERMISSIONS; - } } diff --git a/src/main/java/fr/xephi/authme/permission/handlers/LuckPermsHandler.java b/src/main/java/fr/xephi/authme/permission/handlers/LuckPermsHandler.java index 7e3c6be06..f3a968815 100644 --- a/src/main/java/fr/xephi/authme/permission/handlers/LuckPermsHandler.java +++ b/src/main/java/fr/xephi/authme/permission/handlers/LuckPermsHandler.java @@ -1,22 +1,26 @@ package fr.xephi.authme.permission.handlers; -import fr.xephi.authme.OfflinePlayerWrapper; +import fr.xephi.authme.OfflinePlayerInfo; import fr.xephi.authme.permission.PermissionNode; import fr.xephi.authme.permission.PermissionsSystemType; +import fr.xephi.authme.util.OptionalUtils; import me.lucko.luckperms.LuckPerms; import me.lucko.luckperms.api.Contexts; import me.lucko.luckperms.api.DataMutateResult; import me.lucko.luckperms.api.Group; import me.lucko.luckperms.api.LuckPermsApi; import me.lucko.luckperms.api.Node; +import me.lucko.luckperms.api.PermissionHolder; import me.lucko.luckperms.api.User; import me.lucko.luckperms.api.caching.PermissionData; import me.lucko.luckperms.api.caching.UserData; -import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; +import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; +import java.util.function.BiFunction; import java.util.stream.Collectors; import static fr.xephi.authme.util.OptionalUtils.handleOptional; @@ -50,11 +54,16 @@ public class LuckPermsHandler implements PermissionHandler { return true; } - private CompletableFuture getUser(OfflinePlayerWrapper player) { - if (player.isOnline()) { //FIXME: how to get this? :/ - return completedFuture(luckPermsApi.getUser(player.getUniqueId())); + private Optional getUser(Player player) { + return luckPermsApi.getUserSafe(player.getUniqueId()); + } + + private CompletableFuture getUserOffline(OfflinePlayerInfo offlineInfo) { + if (!offlineInfo.getUniqueId().isPresent()) { + throw new IllegalStateException("Tried to obtain an offline LuckPerms User but the" + + "server doesn't support UUIDs!"); } - return luckPermsApi.getUserManager().loadUser(player.getUniqueId(), player.getName()); + return luckPermsApi.getUserManager().loadUser(offlineInfo.getUniqueId().get(), offlineInfo.getName()); } private Optional getGroup(String groupName) { @@ -65,14 +74,73 @@ public class LuckPermsHandler implements PermissionHandler { return luckPermsApi.getNodeFactory().makeGroupNode(group).build(); } + private T processUser(Player player, String groupName, BiFunction action, T failed) { + return handleOptional(getGroup(groupName), + group -> OptionalUtils.handleOptional(getUser(player), + user -> action.apply(user, group), + () -> failed + ), + () -> failed + ); + } + + private CompletableFuture processUser(OfflinePlayerInfo offlineInfo, String groupName, BiFunction action, T failed) { + return handleOptional(getGroup(groupName), + group -> OptionalUtils.handleOptional(getUser(player), + user -> action.apply(user, group), + () -> failed + ), + () -> failed + ); + } + private void saveUser(User user) { luckPermsApi.getUserManager().saveUser(user); // Async, handled by LuckPerms } + private List getGroupsOrdered(User user) { + return user.getOwnNodes().stream() + .filter(Node::isGroupNode) + .map(node -> luckPermsApi.getGroupSafe(node.getGroupName())) + .filter(Optional::isPresent) + .map(Optional::get) + .distinct() + .sorted((group1, group2) -> { + if (group1.getName().equals(user.getPrimaryGroup()) || group2.getName().equals(user.getPrimaryGroup())) { + return group1.getName().equals(user.getPrimaryGroup()) ? 1 : -1; + } + + int i = Integer.compare(group2.getWeight().orElse(0), group1.getWeight().orElse(0)); + return i != 0 ? i : group1.getName().compareToIgnoreCase(group2.getName()); + }) + .map(Group::getName) + .collect(Collectors.toList()); + } + @Override - public CompletableFuture addToGroup(OfflinePlayerWrapper player, String groupName) { + public CompletableFuture hasPermissionOffline(OfflinePlayerInfo offlineInfo, PermissionNode node) { + return getUserOffline(offlineInfo).thenApply(user -> { + UserData userData = user.getCachedData(); + PermissionData permissionData = userData.getPermissionData(Contexts.allowAll()); + return permissionData.getPermissionValue(node.getNode()).asBoolean(); + }); + } + + @Override + public boolean addToGroup(Player player, String groupName) { + return processUser(player, groupName, ((user, group) -> { + if (user.setPermission(getGroupNode(group)).wasFailure()) { + return false; + } + saveUser(user); + return true; + }), false); + } + + @Override + public CompletableFuture addToGroupOffline(OfflinePlayerInfo offlineInfo, String groupName) { return handleOptional(getGroup(groupName), - group -> getUser(player).thenApply(user -> { + group -> getUserOffline(offlineInfo).thenApply(user -> { if (user.setPermission(getGroupNode(group)).wasFailure()) { return false; } @@ -84,26 +152,33 @@ public class LuckPermsHandler implements PermissionHandler { } @Override - public CompletableFuture hasPermissionOffline(OfflinePlayerWrapper player, PermissionNode node) { - return getUser(player).thenApply(user -> { - UserData userData = user.getCachedData(); - PermissionData permissionData = userData.getPermissionData(Contexts.allowAll()); - return permissionData.getPermissionValue(node.getNode()).asBoolean(); - }); + public boolean isInGroup(Player player, String groupName) { + return processUser(player, groupName, (PermissionHolder::inheritsGroup), false); } @Override - public CompletableFuture isInGroup(OfflinePlayerWrapper player, String groupName) { + public CompletableFuture isInGroupOffline(OfflinePlayerInfo offlineInfo, String groupName) { return handleOptional(luckPermsApi.getGroupSafe(groupName), - group -> getUser(player).thenApply(user -> user.inheritsGroup(group)), + group -> getUserOffline(offlineInfo).thenApply(user -> user.inheritsGroup(group)), () -> completedFuture(false) ); } @Override - public CompletableFuture removeFromGroup(OfflinePlayerWrapper player, String groupName) { + public boolean removeFromGroup(Player player, String groupName) { + return processUser(player, groupName, ((user, group) -> { + if (user.unsetPermission(getGroupNode(group)).wasFailure()) { + return false; + } + saveUser(user); + return true; + }), false); + } + + @Override + public CompletableFuture removeFromGroupOffline(OfflinePlayerInfo offlineInfo, String groupName) { return handleOptional(getGroup(groupName), - group -> getUser(player).thenApply(user -> { + group -> getUserOffline(offlineInfo).thenApply(user -> { if (user.unsetPermission(getGroupNode(group)).wasFailure()) { return false; } @@ -115,9 +190,21 @@ public class LuckPermsHandler implements PermissionHandler { } @Override - public CompletableFuture setGroup(OfflinePlayerWrapper player, String groupName) { + public boolean setGroup(Player player, String groupName) { + return processUser(player, groupName, (user, group) -> { + if (user.setPermission(getGroupNode(group)) == DataMutateResult.FAIL) { + return false; + } + user.clearMatching(node -> node.isGroupNode() && !node.getGroupName().equals(group.getName())); + saveUser(user); + return true; + }, false); + } + + @Override + public CompletableFuture setGroupOffline(OfflinePlayerInfo offlineInfo, String groupName) { return handleOptional(getGroup(groupName), - group -> getUser(player).thenApply(user -> { + group -> getUserOffline(offlineInfo).thenApply(user -> { if (user.setPermission(getGroupNode(group)) == DataMutateResult.FAIL) { return false; } @@ -130,23 +217,13 @@ public class LuckPermsHandler implements PermissionHandler { } @Override - public CompletableFuture> getGroups(OfflinePlayerWrapper player) { - return getUser(player).thenApply(user -> user.getOwnNodes().stream() - .filter(Node::isGroupNode) - .map(n -> luckPermsApi.getGroupSafe(n.getGroupName())) - .filter(Optional::isPresent) - .map(Optional::get) - .distinct() - .sorted((o1, o2) -> { - if (o1.getName().equals(user.getPrimaryGroup()) || o2.getName().equals(user.getPrimaryGroup())) { - return o1.getName().equals(user.getPrimaryGroup()) ? 1 : -1; - } + public List getGroups(Player player) { + return handleOptional(getUser(player), this::getGroupsOrdered, Collections::emptyList); + } - int i = Integer.compare(o2.getWeight().orElse(0), o1.getWeight().orElse(0)); - return i != 0 ? i : o1.getName().compareToIgnoreCase(o2.getName()); - }) - .map(Group::getName) - .collect(Collectors.toList())); + @Override + public CompletableFuture> getGroupsOffline(OfflinePlayerInfo offlineInfo) { + return getUserOffline(offlineInfo).thenApply(this::getGroupsOrdered); } } diff --git a/src/main/java/fr/xephi/authme/permission/handlers/PermissionHandler.java b/src/main/java/fr/xephi/authme/permission/handlers/PermissionHandler.java index 398654320..39d378e00 100644 --- a/src/main/java/fr/xephi/authme/permission/handlers/PermissionHandler.java +++ b/src/main/java/fr/xephi/authme/permission/handlers/PermissionHandler.java @@ -1,9 +1,9 @@ package fr.xephi.authme.permission.handlers; -import fr.xephi.authme.OfflinePlayerWrapper; +import fr.xephi.authme.OfflinePlayerInfo; import fr.xephi.authme.permission.PermissionNode; import fr.xephi.authme.permission.PermissionsSystemType; -import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; import java.util.Collection; import java.util.List; @@ -31,23 +31,62 @@ public interface PermissionHandler { * Check if a player has permission by their name. * Used to check an offline player's permission. * - * @param player The offline player. + * @param offlineInfo The offline player info. * @param node The permission node. * * @return True if the player has permission. */ - CompletableFuture hasPermissionOffline(OfflinePlayerWrapper player, PermissionNode node); + CompletableFuture hasPermissionOffline(OfflinePlayerInfo offlineInfo, PermissionNode node); /** - * Add the permission group of a player, if supported. + * Get the permission groups of a player, if available. * - * @param player The player - * @param groupName The name of the group. + * @param player The player. * - * @return True if succeed, false otherwise. - * False is also returned if this feature isn't supported for the current permissions system. + * @return Permission groups, or an empty list if this feature is not supported. */ - CompletableFuture addToGroup(OfflinePlayerWrapper player, String groupName); + List getGroups(Player player); + + /** + * Get the permission groups of an offline player, if available. + * + * @param offlineInfo The offline player info. + * + * @return Permission groups, or an empty list if this feature is not supported. + */ + CompletableFuture> getGroupsOffline(OfflinePlayerInfo offlineInfo); + + /** + * Get the primary group of a player, if available. + * + * @param player The player. + * + * @return The optional name of the primary permission group, empty if not found. + */ + default Optional getPrimaryGroup(Player player) { + Collection groups = getGroups(player); + if(groups.isEmpty()) { + return Optional.empty(); + } + return Optional.of(groups.iterator().next()); + } + + /** + * Get the primary group of a player, if available. + * + * @param offlineInfo The offline player info. + * + * @return The optional name of the primary permission group, empty if not found. + */ + default CompletableFuture> getPrimaryGroupOffline(OfflinePlayerInfo offlineInfo) { + return CompletableFuture.supplyAsync(() -> { + Collection groups = getGroupsOffline(offlineInfo).join(); + if(groups.isEmpty()) { + return Optional.empty(); + } + return Optional.of(groups.iterator().next()); + }); + } /** * Check whether the player is in the specified group. @@ -58,10 +97,45 @@ public interface PermissionHandler { * @return True if the player is in the specified group, false otherwise. * False is also returned if groups aren't supported by the used permissions system. */ - default CompletableFuture isInGroup(OfflinePlayerWrapper player, String groupName) { - return CompletableFuture.supplyAsync(() -> getGroups(player).join().contains(groupName)); + default boolean isInGroup(Player player, String groupName) { + return getGroups(player).contains(groupName); } + /** + * Check whether the player is in the specified group. + * + * @param offlineInfo The player. + * @param groupName The group name. + * + * @return True if the player is in the specified group, false otherwise. + * False is also returned if groups aren't supported by the used permissions system. + */ + default CompletableFuture isInGroupOffline(OfflinePlayerInfo offlineInfo, String groupName) { + return CompletableFuture.supplyAsync(() -> getGroupsOffline(offlineInfo).join().contains(groupName)); + } + + /** + * Add the permission group of a player, if supported. + * + * @param player The player + * @param groupName The name of the group. + * + * @return True if succeed, false otherwise. + * False is also returned if this feature isn't supported for the current permissions system. + */ + boolean addToGroup(Player player, String groupName); + + /** + * Add the permission group of a player, if supported. + * + * @param offlineInfo The offline player info. + * @param groupName The name of the group. + * + * @return True if succeed, false otherwise. + * False is also returned if this feature isn't supported for the current permissions system. + */ + CompletableFuture addToGroupOffline(OfflinePlayerInfo offlineInfo, String groupName); + /** * Remove the permission group of a player, if supported. * @@ -71,44 +145,41 @@ public interface PermissionHandler { * @return True if succeed, false otherwise. * False is also returned if this feature isn't supported for the current permissions system. */ - CompletableFuture removeFromGroup(OfflinePlayerWrapper player, String groupName); + boolean removeFromGroup(Player player, String groupName); + + /** + * Remove the permission group of a player, if supported. + * + * @param playerInfo The offline player info. + * @param groupName The name of the group. + * + * @return True if succeed, false otherwise. + * False is also returned if this feature isn't supported for the current permissions system. + */ + CompletableFuture removeFromGroupOffline(OfflinePlayerInfo playerInfo, String groupName); /** * Set the permission group of a player, if supported. * This clears the current groups of the player. * - * @param player The player - * @param group The name of the group. + * @param player The player. + * @param group The name of the group. * * @return True if succeed, false otherwise. * False is also returned if this feature isn't supported for the current permissions system. */ - CompletableFuture setGroup(OfflinePlayerWrapper player, String group); + boolean setGroup(Player player, String group); /** - * Get the permission groups of a player, if available. + * Set the permission group of a player, if supported. + * This clears the current groups of the player. * - * @param player The player. + * @param offlineInfo The offline player info. + * @param group The name of the group. * - * @return Permission groups, or an empty list if this feature is not supported. + * @return True if succeed, false otherwise. + * False is also returned if this feature isn't supported for the current permissions system. */ - CompletableFuture> getGroups(OfflinePlayerWrapper player); - - /** - * Get the primary group of a player, if available. - * - * @param player The player. - * - * @return The name of the primary permission group. Or null. - */ - default CompletableFuture> getPrimaryGroup(OfflinePlayerWrapper player) { - return CompletableFuture.supplyAsync(() -> { - Collection groups = getGroups(player).join(); - if(groups.isEmpty()) { - return Optional.empty(); - } - return Optional.of(groups.iterator().next()); - }); - } + CompletableFuture setGroupOffline(OfflinePlayerInfo offlineInfo, String group); }