From 53fb46ee8540565d9652c7e7ed3a2d32e1f0891b Mon Sep 17 00:00:00 2001 From: Luck Date: Mon, 23 Nov 2020 23:54:02 +0000 Subject: [PATCH] Expose uuid/username lookups and validity checks as events in the API --- .../lookup/UniqueIdDetermineTypeEvent.java | 86 ++++++++++++++ .../player/lookup/UniqueIdLookupEvent.java | 61 ++++++++++ .../player/lookup/UsernameLookupEvent.java | 61 ++++++++++ .../lookup/UsernameValidityCheckEvent.java | 76 ++++++++++++ .../luckperms/api/event/type/ResultEvent.java | 59 ++++++++++ .../vault/LuckPermsVaultPermission.java | 14 +-- .../commands/group/GroupListMembers.java | 17 +-- .../common/commands/misc/SearchCommand.java | 17 +-- .../common/commands/user/UserInfo.java | 3 +- .../commands/user/UserParentCommand.java | 45 +++----- .../common/event/EventDispatcher.java | 49 ++++++++ .../luckperms/common/locale/Message.java | 4 +- .../plugin/AbstractLuckPermsPlugin.java | 49 ++++++++ .../common/plugin/LuckPermsPlugin.java | 25 ++++ .../luckperms/common/util/UniqueIdType.java | 109 ++++++++++++++++++ 15 files changed, 601 insertions(+), 74 deletions(-) create mode 100644 api/src/main/java/net/luckperms/api/event/player/lookup/UniqueIdDetermineTypeEvent.java create mode 100644 api/src/main/java/net/luckperms/api/event/player/lookup/UniqueIdLookupEvent.java create mode 100644 api/src/main/java/net/luckperms/api/event/player/lookup/UsernameLookupEvent.java create mode 100644 api/src/main/java/net/luckperms/api/event/player/lookup/UsernameValidityCheckEvent.java create mode 100644 api/src/main/java/net/luckperms/api/event/type/ResultEvent.java create mode 100644 common/src/main/java/me/lucko/luckperms/common/util/UniqueIdType.java diff --git a/api/src/main/java/net/luckperms/api/event/player/lookup/UniqueIdDetermineTypeEvent.java b/api/src/main/java/net/luckperms/api/event/player/lookup/UniqueIdDetermineTypeEvent.java new file mode 100644 index 000000000..78bec2de9 --- /dev/null +++ b/api/src/main/java/net/luckperms/api/event/player/lookup/UniqueIdDetermineTypeEvent.java @@ -0,0 +1,86 @@ +/* + * 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 net.luckperms.api.event.player.lookup; + +import net.luckperms.api.event.LuckPermsEvent; +import net.luckperms.api.event.type.ResultEvent; +import net.luckperms.api.event.util.Param; + +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.util.Objects; +import java.util.UUID; + +/** + * Called when the platform needs to determine the type of a player's {@link UUID unique id}. + * + * @since 5.3 + */ +public interface UniqueIdDetermineTypeEvent extends LuckPermsEvent, ResultEvent { + + /** + * The players UUID has been obtained by authenticating with the Mojang session servers. + * + *

Usually indicated by the UUID being {@link UUID#version() version} 4.

+ */ + String TYPE_AUTHENTICATED = "authenticated"; + + /** + * The players UUID has not been obtained through authentication, and instead is likely based + * on the username they connected with. + * + *

Usually indicated by the UUID being {@link UUID#version() version} 3.

+ */ + String TYPE_UNAUTHENTICATED = "unauthenticated"; + + /** + * Gets the {@link UUID unique id} being queried. + * + * @return the unique id + */ + @Param(0) + @NonNull UUID getUniqueId(); + + /** + * Gets the current result unique id type. + * + * @return the type + */ + default @NonNull String getType() { + return result().get(); + } + + /** + * Sets the result unique id type. + * + * @param type the type + */ + default void setType(@NonNull String type) { + Objects.requireNonNull(type, "type"); + result().set(type); + } + +} diff --git a/api/src/main/java/net/luckperms/api/event/player/lookup/UniqueIdLookupEvent.java b/api/src/main/java/net/luckperms/api/event/player/lookup/UniqueIdLookupEvent.java new file mode 100644 index 000000000..e351926f3 --- /dev/null +++ b/api/src/main/java/net/luckperms/api/event/player/lookup/UniqueIdLookupEvent.java @@ -0,0 +1,61 @@ +/* + * 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 net.luckperms.api.event.player.lookup; + +import net.luckperms.api.event.LuckPermsEvent; +import net.luckperms.api.event.type.ResultEvent; +import net.luckperms.api.event.util.Param; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.util.UUID; + +/** + * Called when the platform needs a unique id for a given username. + * + * @since 5.3 + */ +public interface UniqueIdLookupEvent extends LuckPermsEvent, ResultEvent { + + /** + * Gets the username being looked up. + * + * @return the username + */ + @Param(0) + @NonNull String getUsername(); + + /** + * Sets the result unique id. + * + * @param uniqueId the unique id + */ + default void setUniqueId(@Nullable UUID uniqueId) { + result().set(uniqueId); + } + +} diff --git a/api/src/main/java/net/luckperms/api/event/player/lookup/UsernameLookupEvent.java b/api/src/main/java/net/luckperms/api/event/player/lookup/UsernameLookupEvent.java new file mode 100644 index 000000000..4650f080f --- /dev/null +++ b/api/src/main/java/net/luckperms/api/event/player/lookup/UsernameLookupEvent.java @@ -0,0 +1,61 @@ +/* + * 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 net.luckperms.api.event.player.lookup; + +import net.luckperms.api.event.LuckPermsEvent; +import net.luckperms.api.event.type.ResultEvent; +import net.luckperms.api.event.util.Param; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.util.UUID; + +/** + * Called when the platform needs a username for a given unique id. + * + * @since 5.3 + */ +public interface UsernameLookupEvent extends LuckPermsEvent, ResultEvent { + + /** + * Gets the {@link UUID unique id} being looked up. + * + * @return the unique id + */ + @Param(0) + @NonNull UUID getUniqueId(); + + /** + * Sets the result username. + * + * @param username the username + */ + default void setUsername(@Nullable String username) { + result().set(username); + } + +} diff --git a/api/src/main/java/net/luckperms/api/event/player/lookup/UsernameValidityCheckEvent.java b/api/src/main/java/net/luckperms/api/event/player/lookup/UsernameValidityCheckEvent.java new file mode 100644 index 000000000..94b7444d7 --- /dev/null +++ b/api/src/main/java/net/luckperms/api/event/player/lookup/UsernameValidityCheckEvent.java @@ -0,0 +1,76 @@ +/* + * 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 net.luckperms.api.event.player.lookup; + +import net.luckperms.api.event.LuckPermsEvent; +import net.luckperms.api.event.util.Param; + +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Called when the validity of a username is being tested. + * + * @since 5.3 + */ +public interface UsernameValidityCheckEvent extends LuckPermsEvent { + + /** + * Gets the username being tested. + * + * @return the username + */ + @Param(0) + @NonNull String getUsername(); + + /** + * Gets the current validity state for the username. + * + * @return the validity state + */ + @Param(1) + @NonNull AtomicBoolean validityState(); + + /** + * Gets if the username is currently considered to be valid. + * + * @return if the username is valid + */ + default boolean isValid() { + return validityState().get(); + } + + /** + * Sets if the username should be considered valid or not. + * + * @param valid whether the username is valid + */ + default void setValid(boolean valid) { + validityState().set(valid); + } + +} diff --git a/api/src/main/java/net/luckperms/api/event/type/ResultEvent.java b/api/src/main/java/net/luckperms/api/event/type/ResultEvent.java new file mode 100644 index 000000000..685702a6a --- /dev/null +++ b/api/src/main/java/net/luckperms/api/event/type/ResultEvent.java @@ -0,0 +1,59 @@ +/* + * 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 net.luckperms.api.event.type; + +import net.luckperms.api.event.util.Param; + +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.util.concurrent.atomic.AtomicReference; + +/** + * Represents an event that has a result. + * + * @param the type of the result + * @since 5.3 + */ +public interface ResultEvent { + + /** + * Gets an {@link AtomicReference} containing the result. + * + * @return the result + */ + @Param(-1) + @NonNull AtomicReference result(); + + /** + * Gets if a result has been set for the event. + * + * @return if there is a result + */ + default boolean hasResult() { + return result().get() != null; + } + +} diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/LuckPermsVaultPermission.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/LuckPermsVaultPermission.java index c2dc804f1..4cb5e78b5 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/LuckPermsVaultPermission.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/LuckPermsVaultPermission.java @@ -42,6 +42,7 @@ import me.lucko.luckperms.common.model.manager.group.GroupManager; import me.lucko.luckperms.common.node.factory.NodeBuilders; import me.lucko.luckperms.common.node.types.Inheritance; import me.lucko.luckperms.common.query.QueryOptionsImpl; +import me.lucko.luckperms.common.util.UniqueIdType; import me.lucko.luckperms.common.util.Uuids; import me.lucko.luckperms.common.verbose.event.MetaCheckEvent; import me.lucko.luckperms.common.verbose.event.PermissionCheckEvent; @@ -124,10 +125,7 @@ public class LuckPermsVaultPermission extends AbstractVaultPermission { } // lookup a username from the database - uuid = this.plugin.getStorage().getPlayerUniqueId(player.toLowerCase()).join(); - if (uuid == null) { - uuid = this.plugin.getBootstrap().lookupUniqueId(player).orElse(null); - } + uuid = this.plugin.lookupUniqueId(player).orElse(null); // unable to find a user, throw an exception if (uuid == null) { @@ -146,10 +144,8 @@ public class LuckPermsVaultPermission extends AbstractVaultPermission { return user; } - // if the uuid is version 2, assume it is an NPC - // see: https://github.com/lucko/LuckPerms/issues/1470 - // and https://github.com/lucko/LuckPerms/issues/1470#issuecomment-475403162 - if (uuid.version() == 2) { + // is it an npc? + if (UniqueIdType.determineType(uuid, this.plugin).getType().equals("npc")) { String npcGroupName = this.plugin.getConfiguration().get(ConfigKeys.VAULT_NPC_GROUP); Group npcGroup = this.plugin.getGroupManager().getIfLoaded(npcGroupName); if (npcGroup == null) { @@ -380,7 +376,7 @@ public class LuckPermsVaultPermission extends AbstractVaultPermission { boolean op = false; if (player != null) { op = player.isOp(); - } else if (uuid != null && uuid.version() == 2) { // npc + } else if (uuid != null && UniqueIdType.determineType(uuid, this.plugin).getType().equals("npc")) { op = this.plugin.getConfiguration().get(ConfigKeys.VAULT_NPC_OP_STATUS); } diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupListMembers.java b/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupListMembers.java index 7cd44a210..10c41fee3 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupListMembers.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupListMembers.java @@ -34,7 +34,6 @@ import me.lucko.luckperms.common.command.access.ArgumentPermissions; import me.lucko.luckperms.common.command.access.CommandPermission; import me.lucko.luckperms.common.command.spec.CommandSpec; import me.lucko.luckperms.common.command.utils.ArgumentList; -import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.locale.Message; import me.lucko.luckperms.common.model.Group; import me.lucko.luckperms.common.model.HolderType; @@ -104,21 +103,7 @@ public class GroupListMembers extends ChildCommand { Message.SEARCH_RESULT.send(sender, users + groups, users, groups); if (!matchedUsers.isEmpty()) { - Map uuidLookups = LoadingMap.of(u -> { - String s = plugin.getStorage().getPlayerName(u).join(); - if (s != null && !s.isEmpty() && !s.equals("null")) { - return s; - } - - if (plugin.getConfiguration().get(ConfigKeys.USE_SERVER_UUID_CACHE)) { - s = plugin.getBootstrap().lookupUsername(u).orElse(null); - if (s != null) { - return s; - } - } - - return u.toString(); - }); + Map uuidLookups = LoadingMap.of(u -> plugin.lookupUsername(u).orElseGet(u::toString)); sendResult(sender, matchedUsers, uuidLookups::get, Message.SEARCH_SHOWING_USERS, HolderType.USER, label, page); } diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/misc/SearchCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/misc/SearchCommand.java index 0f9d17cb9..a7df7c41a 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/misc/SearchCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/misc/SearchCommand.java @@ -38,7 +38,6 @@ import me.lucko.luckperms.common.command.spec.CommandSpec; import me.lucko.luckperms.common.command.tabcomplete.TabCompleter; import me.lucko.luckperms.common.command.tabcomplete.TabCompletions; import me.lucko.luckperms.common.command.utils.ArgumentList; -import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.locale.Message; import me.lucko.luckperms.common.model.HolderType; import me.lucko.luckperms.common.node.comparator.NodeEntryComparator; @@ -86,21 +85,7 @@ public class SearchCommand extends SingleCommand { Message.SEARCH_RESULT.send(sender, users + groups, users, groups); if (!matchedUsers.isEmpty()) { - Map uuidLookups = LoadingMap.of(u -> { - String s = plugin.getStorage().getPlayerName(u).join(); - if (s != null && !s.isEmpty() && !s.equals("null")) { - return s; - } - - if (plugin.getConfiguration().get(ConfigKeys.USE_SERVER_UUID_CACHE)) { - s = plugin.getBootstrap().lookupUsername(u).orElse(null); - if (s != null) { - return s; - } - } - - return u.toString(); - }); + Map uuidLookups = LoadingMap.of(u -> plugin.lookupUsername(u).orElseGet(u::toString)); sendResult(sender, matchedUsers, uuidLookups::get, Message.SEARCH_SHOWING_USERS, HolderType.USER, label, page, comparison); } diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/user/UserInfo.java b/common/src/main/java/me/lucko/luckperms/common/commands/user/UserInfo.java index 8f9803989..bfc45cbae 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/user/UserInfo.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/user/UserInfo.java @@ -37,6 +37,7 @@ import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.sender.Sender; import me.lucko.luckperms.common.util.Predicates; +import me.lucko.luckperms.common.util.UniqueIdType; import me.lucko.luckperms.common.verbose.event.MetaCheckEvent; import net.luckperms.api.context.ContextSet; @@ -63,7 +64,7 @@ public class UserInfo extends ChildCommand { Message.USER_INFO_GENERAL.send(sender, target.getUsername().orElse("Unknown"), target.getUniqueId().toString(), - target.getUniqueId().version() == 4, + UniqueIdType.determineType(target.getUniqueId(), plugin).describe(), plugin.getBootstrap().isPlayerOnline(target.getUniqueId()) ); diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/user/UserParentCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/user/UserParentCommand.java index 80c1969bf..64d2ce346 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/user/UserParentCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/user/UserParentCommand.java @@ -37,14 +37,12 @@ import me.lucko.luckperms.common.commands.generic.other.HolderEditor; import me.lucko.luckperms.common.commands.generic.other.HolderShowTracks; import me.lucko.luckperms.common.commands.generic.parent.CommandParent; import me.lucko.luckperms.common.commands.generic.permission.CommandPermission; -import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.locale.Message; import me.lucko.luckperms.common.model.HolderType; import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.model.UserIdentifier; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.sender.Sender; -import me.lucko.luckperms.common.storage.misc.DataConstraints; import me.lucko.luckperms.common.util.CaffeineFactory; import me.lucko.luckperms.common.util.Uuids; @@ -81,36 +79,23 @@ public class UserParentCommand extends ParentCommand { } public static UUID parseTargetUniqueId(String target, LuckPermsPlugin plugin, Sender sender) { - UUID uniqueId = Uuids.parse(target); - if (uniqueId == null) { - if (!plugin.getConfiguration().get(ConfigKeys.ALLOW_INVALID_USERNAMES)) { - if (!DataConstraints.PLAYER_USERNAME_TEST.test(target)) { - Message.USER_INVALID_ENTRY.send(sender, target); - return null; - } - } else { - if (!DataConstraints.PLAYER_USERNAME_TEST_LENIENT.test(target)) { - Message.USER_INVALID_ENTRY.send(sender, target); - return null; - } - } - - uniqueId = plugin.getStorage().getPlayerUniqueId(target.toLowerCase()).join(); - if (uniqueId == null) { - if (!plugin.getConfiguration().get(ConfigKeys.USE_SERVER_UUID_CACHE)) { - Message.USER_NOT_FOUND.send(sender, target); - return null; - } - - uniqueId = plugin.getBootstrap().lookupUniqueId(target).orElse(null); - if (uniqueId == null) { - Message.USER_NOT_FOUND.send(sender, target); - return null; - } - } + UUID parsed = Uuids.parse(target); + if (parsed != null) { + return parsed; } - return uniqueId; + if (!plugin.testUsernameValidity(target)) { + Message.USER_INVALID_ENTRY.send(sender, target); + return null; + } + + UUID lookup = plugin.lookupUniqueId(target).orElse(null); + if (lookup == null) { + Message.USER_NOT_FOUND.send(sender, target); + return null; + } + + return lookup; } @Override diff --git a/common/src/main/java/me/lucko/luckperms/common/event/EventDispatcher.java b/common/src/main/java/me/lucko/luckperms/common/event/EventDispatcher.java index 735e40c5e..e1130e25d 100644 --- a/common/src/main/java/me/lucko/luckperms/common/event/EventDispatcher.java +++ b/common/src/main/java/me/lucko/luckperms/common/event/EventDispatcher.java @@ -64,6 +64,10 @@ import net.luckperms.api.event.node.NodeClearEvent; import net.luckperms.api.event.node.NodeRemoveEvent; import net.luckperms.api.event.player.PlayerDataSaveEvent; import net.luckperms.api.event.player.PlayerLoginProcessEvent; +import net.luckperms.api.event.player.lookup.UniqueIdDetermineTypeEvent; +import net.luckperms.api.event.player.lookup.UniqueIdLookupEvent; +import net.luckperms.api.event.player.lookup.UsernameLookupEvent; +import net.luckperms.api.event.player.lookup.UsernameValidityCheckEvent; import net.luckperms.api.event.source.Source; import net.luckperms.api.event.sync.ConfigReloadEvent; import net.luckperms.api.event.sync.PostSyncEvent; @@ -95,6 +99,7 @@ import java.util.List; import java.util.Optional; import java.util.UUID; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.Supplier; public final class EventDispatcher { @@ -313,6 +318,46 @@ public final class EventDispatcher { post(PlayerDataSaveEvent.class, () -> generate(PlayerDataSaveEvent.class, uniqueId, username, result)); } + public String dispatchUniqueIdDetermineType(UUID uniqueId, String initialType) { + if (!shouldPost(UniqueIdDetermineTypeEvent.class)) { + return initialType; + } + + AtomicReference result = new AtomicReference<>(initialType); + post(generate(UniqueIdDetermineTypeEvent.class, result, uniqueId)); + return result.get(); + } + + public UUID dispatchUniqueIdLookup(String username, UUID initial) { + if (!shouldPost(UniqueIdLookupEvent.class)) { + return initial; + } + + AtomicReference result = new AtomicReference<>(initial); + post(generate(UniqueIdLookupEvent.class, result, username)); + return result.get(); + } + + public String dispatchUsernameLookup(UUID uniqueId, String initial) { + if (!shouldPost(UsernameLookupEvent.class)) { + return initial; + } + + AtomicReference result = new AtomicReference<>(initial); + post(generate(UsernameLookupEvent.class, result, uniqueId)); + return result.get(); + } + + public boolean dispatchUsernameValidityCheck(String username, boolean initialState) { + if (!shouldPost(UsernameValidityCheckEvent.class)) { + return initialState; + } + + AtomicBoolean result = new AtomicBoolean(initialState); + post(generate(UsernameValidityCheckEvent.class, username, result)); + return result.get(); + } + public void dispatchUserLoad(User user) { post(UserLoadEvent.class, () -> generate(UserLoadEvent.class, user.getApiProxy())); } @@ -361,6 +406,10 @@ public final class EventDispatcher { NodeRemoveEvent.class, PlayerDataSaveEvent.class, PlayerLoginProcessEvent.class, + UniqueIdDetermineTypeEvent.class, + UniqueIdLookupEvent.class, + UsernameLookupEvent.class, + UsernameValidityCheckEvent.class, ConfigReloadEvent.class, PostSyncEvent.class, PreNetworkSyncEvent.class, diff --git a/common/src/main/java/me/lucko/luckperms/common/locale/Message.java b/common/src/main/java/me/lucko/luckperms/common/locale/Message.java index 662bc428d..c2027ee6c 100644 --- a/common/src/main/java/me/lucko/luckperms/common/locale/Message.java +++ b/common/src/main/java/me/lucko/luckperms/common/locale/Message.java @@ -2757,7 +2757,7 @@ public interface Message { .append(FULL_STOP) ); - Args4 USER_INFO_GENERAL = (username, uuid, mojang, online) -> join(newline(), + Args4 USER_INFO_GENERAL = (username, uuid, uuidType, online) -> join(newline(), // "&b&l> &bUser Info: &f{}" // "&f- &3UUID: &f{}" // "&f &7(type: {}&7)" @@ -2781,7 +2781,7 @@ public interface Message { .append(OPEN_BRACKET) .append(translatable("luckperms.command.user.info.uuid-type-key")) .append(text(": ")) - .append(mojang ? translatable("luckperms.command.user.info.uuid-type.mojang", DARK_GREEN) : translatable("luckperms.command.user.info.uuid-type.not-mojang", DARK_GRAY)) + .append(uuidType) .append(CLOSE_BRACKET)), prefixed(text() .color(DARK_AQUA) diff --git a/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java b/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java index 8cb03ab1c..28ee1850d 100644 --- a/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java +++ b/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java @@ -51,6 +51,7 @@ import me.lucko.luckperms.common.storage.Storage; import me.lucko.luckperms.common.storage.StorageFactory; import me.lucko.luckperms.common.storage.StorageType; import me.lucko.luckperms.common.storage.implementation.file.watcher.FileWatcher; +import me.lucko.luckperms.common.storage.misc.DataConstraints; import me.lucko.luckperms.common.tasks.SyncTask; import me.lucko.luckperms.common.treeview.PermissionRegistry; import me.lucko.luckperms.common.verbose.VerboseHandler; @@ -66,6 +67,7 @@ import java.time.Month; import java.util.EnumSet; import java.util.Optional; import java.util.Set; +import java.util.UUID; import java.util.concurrent.TimeUnit; public abstract class AbstractLuckPermsPlugin implements LuckPermsPlugin { @@ -291,6 +293,53 @@ public abstract class AbstractLuckPermsPlugin implements LuckPermsPlugin { } } + @Override + public Optional lookupUniqueId(String username) { + // get a result from the DB cache + UUID uniqueId = getStorage().getPlayerUniqueId(username.toLowerCase()).join(); + + // fire the event + uniqueId = getEventDispatcher().dispatchUniqueIdLookup(username, uniqueId); + + // try the servers cache + if (uniqueId == null && getConfiguration().get(ConfigKeys.USE_SERVER_UUID_CACHE)) { + uniqueId = getBootstrap().lookupUniqueId(username).orElse(null); + } + + return Optional.ofNullable(uniqueId); + } + + @Override + public Optional lookupUsername(UUID uniqueId) { + // get a result from the DB cache + String username = getStorage().getPlayerName(uniqueId).join(); + + // fire the event + username = getEventDispatcher().dispatchUsernameLookup(uniqueId, username); + + // try the servers cache + if (username == null && getConfiguration().get(ConfigKeys.USE_SERVER_UUID_CACHE)) { + username = getBootstrap().lookupUsername(uniqueId).orElse(null); + } + + return Optional.ofNullable(username); + } + + @Override + public boolean testUsernameValidity(String username) { + // if the username doesn't even pass the lenient test, don't bother going any further + // it's either empty, or too long to fit in the sql column + if (!DataConstraints.PLAYER_USERNAME_TEST_LENIENT.test(username)) { + return false; + } + + // if invalid usernames are allowed in the config, set valid to true, otherwise, use the more strict test + boolean valid = getConfiguration().get(ConfigKeys.ALLOW_INVALID_USERNAMES) || DataConstraints.PLAYER_USERNAME_TEST.test(username); + + // fire the event & return + return getEventDispatcher().dispatchUsernameValidityCheck(username, valid); + } + @Override public DependencyManager getDependencyManager() { return this.dependencyManager; diff --git a/common/src/main/java/me/lucko/luckperms/common/plugin/LuckPermsPlugin.java b/common/src/main/java/me/lucko/luckperms/common/plugin/LuckPermsPlugin.java index 718261baa..d002d2ece 100644 --- a/common/src/main/java/me/lucko/luckperms/common/plugin/LuckPermsPlugin.java +++ b/common/src/main/java/me/lucko/luckperms/common/plugin/LuckPermsPlugin.java @@ -61,6 +61,7 @@ import net.luckperms.api.query.QueryOptions; import java.util.Collections; import java.util.List; import java.util.Optional; +import java.util.UUID; import java.util.stream.Stream; /** @@ -255,6 +256,30 @@ public interface LuckPermsPlugin { */ Optional getQueryOptionsForUser(User user); + /** + * Lookup a uuid from a username. + * + * @param username the username to lookup + * @return an optional uuid, if found + */ + Optional lookupUniqueId(String username); + + /** + * Lookup a username from a uuid. + * + * @param uniqueId the uuid to lookup + * @return an optional username, if found + */ + Optional lookupUsername(UUID uniqueId); + + /** + * Tests whether the given username is valid. + * + * @param username the username + * @return true if valid + */ + boolean testUsernameValidity(String username); + /** * Gets a list of online Senders on the platform * diff --git a/common/src/main/java/me/lucko/luckperms/common/util/UniqueIdType.java b/common/src/main/java/me/lucko/luckperms/common/util/UniqueIdType.java new file mode 100644 index 000000000..b3dbfdd89 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/util/UniqueIdType.java @@ -0,0 +1,109 @@ +/* + * 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.common.util; + +import me.lucko.luckperms.common.plugin.LuckPermsPlugin; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.luckperms.api.event.player.lookup.UniqueIdDetermineTypeEvent; + +import java.util.UUID; + +/** + * Encapsulates the type of a players unique id. + */ +public final class UniqueIdType { + + public static final UniqueIdType AUTHENTICATED = new UniqueIdType( + UniqueIdDetermineTypeEvent.TYPE_AUTHENTICATED, + Component.translatable("luckperms.command.user.info.uuid-type.mojang", NamedTextColor.DARK_GREEN) + ); + + public static final UniqueIdType UNAUTHENTICATED = new UniqueIdType( + UniqueIdDetermineTypeEvent.TYPE_UNAUTHENTICATED, + Component.translatable("luckperms.command.user.info.uuid-type.not-mojang", NamedTextColor.DARK_GRAY) + ); + + private static final String TYPE_NPC = "npc"; + public static final UniqueIdType NPC = new UniqueIdType(TYPE_NPC); + + public static UniqueIdType determineType(UUID uniqueId, LuckPermsPlugin plugin) { + // determine initial type based on the uuid version + String type; + switch (uniqueId.version()) { + case 4: + type = UniqueIdDetermineTypeEvent.TYPE_AUTHENTICATED; + break; + case 3: + type = UniqueIdDetermineTypeEvent.TYPE_UNAUTHENTICATED; + break; + case 2: + // if the uuid is version 2, assume it is an NPC + // see: https://github.com/lucko/LuckPerms/issues/1470 + // and https://github.com/lucko/LuckPerms/issues/1470#issuecomment-475403162 + type = TYPE_NPC; + break; + default: + type = "unknown"; + break; + } + + // call the event + type = plugin.getEventDispatcher().dispatchUniqueIdDetermineType(uniqueId, type); + + switch (type) { + case UniqueIdDetermineTypeEvent.TYPE_AUTHENTICATED: + return AUTHENTICATED; + case UniqueIdDetermineTypeEvent.TYPE_UNAUTHENTICATED: + return UNAUTHENTICATED; + case TYPE_NPC: + return NPC; + default: + return new UniqueIdType(type); + } + } + + private final String type; + private final Component component; + + private UniqueIdType(String type) { + this(type, Component.text(type, NamedTextColor.GOLD)); + } + + private UniqueIdType(String type, Component component) { + this.type = type; + this.component = component; + } + + public String getType() { + return this.type; + } + + public Component describe() { + return this.component; + } +}