diff --git a/api/src/main/java/com/velocitypowered/api/permission/PermissionSubject.java b/api/src/main/java/com/velocitypowered/api/permission/PermissionSubject.java index ec7171e9f..b117f7385 100644 --- a/api/src/main/java/com/velocitypowered/api/permission/PermissionSubject.java +++ b/api/src/main/java/com/velocitypowered/api/permission/PermissionSubject.java @@ -7,6 +7,9 @@ package com.velocitypowered.api.permission; +import net.kyori.adventure.permission.PermissionChecker; +import net.kyori.adventure.util.TriState; + /** * Represents a object that has a set of queryable permissions. */ @@ -29,4 +32,13 @@ public interface PermissionSubject { * @return the value the permission is set to */ Tristate getPermissionValue(String permission); + + /** + * Gets the permission checker for the subject. + * + * @return subject's permission checker + */ + default PermissionChecker getPermissionChecker() { + return permission -> getPermissionValue(permission).toAdventureTriState(); + } } diff --git a/api/src/main/java/com/velocitypowered/api/permission/Tristate.java b/api/src/main/java/com/velocitypowered/api/permission/Tristate.java index 70653eef6..9ad1482a7 100644 --- a/api/src/main/java/com/velocitypowered/api/permission/Tristate.java +++ b/api/src/main/java/com/velocitypowered/api/permission/Tristate.java @@ -7,6 +7,7 @@ package com.velocitypowered.api.permission; +import net.kyori.adventure.util.TriState; import org.checkerframework.checker.nullness.qual.Nullable; /** @@ -81,4 +82,21 @@ public enum Tristate { public boolean asBoolean() { return this.booleanValue; } + + /** + * Returns the equivalent Adventure {@link TriState}. + * + * @return equivalent Adventure TriState + */ + public TriState toAdventureTriState() { + if (this == Tristate.TRUE) { + return TriState.TRUE; + } else if (this == Tristate.UNDEFINED) { + return TriState.NOT_SET; + } else if (this == Tristate.FALSE) { + return TriState.FALSE; + } else { + throw new IllegalArgumentException(); + } + } } diff --git a/api/src/main/java/com/velocitypowered/api/proxy/Player.java b/api/src/main/java/com/velocitypowered/api/proxy/Player.java index 51d966044..66c5b433a 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/Player.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/Player.java @@ -23,16 +23,22 @@ import com.velocitypowered.api.util.title.Title; import java.util.List; import java.util.Optional; import java.util.UUID; +import java.util.function.UnaryOperator; import net.kyori.adventure.identity.Identified; import net.kyori.adventure.identity.Identity; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.key.Keyed; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.event.HoverEvent; +import net.kyori.adventure.text.event.HoverEventSource; import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.NotNull; /** * Represents a player who is connected to the proxy. */ public interface Player extends CommandSource, Identified, InboundConnection, - ChannelMessageSource, ChannelMessageSink { + ChannelMessageSource, ChannelMessageSink, HoverEventSource, Keyed { /** * Returns the player's current username. @@ -275,4 +281,16 @@ public interface Player extends CommandSource, Identified, InboundConnection, */ @Override boolean sendPluginMessage(ChannelIdentifier identifier, byte[] data); + + @Override + default @NotNull Key key() { + return Key.key("player"); + } + + @Override + default @NotNull HoverEvent asHoverEvent( + @NotNull UnaryOperator op) { + return HoverEvent.showEntity(op.apply(HoverEvent.ShowEntity.of(this, getUniqueId(), + Component.text(getUsername())))); + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java index 64bb15122..f0f31f0ed 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java @@ -94,6 +94,8 @@ import java.util.concurrent.ThreadLocalRandom; import net.kyori.adventure.audience.MessageType; import net.kyori.adventure.bossbar.BossBar; import net.kyori.adventure.identity.Identity; +import net.kyori.adventure.permission.PermissionChecker; +import net.kyori.adventure.pointer.Pointers; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TranslatableComponent; import net.kyori.adventure.text.format.NamedTextColor; @@ -105,6 +107,7 @@ import org.apache.logging.log4j.Logger; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.NotNull; public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { @@ -142,6 +145,11 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { private final Queue outstandingResourcePacks = new ArrayDeque<>(); private @Nullable ResourcePackInfo pendingResourcePack; private @Nullable ResourcePackInfo appliedResourcePack; + private final @NotNull Pointers pointers = Player.super.pointers().toBuilder() + .withDynamic(Identity.UUID, this::getUniqueId) + .withDynamic(Identity.NAME, this::getUsername) + .withStatic(PermissionChecker.POINTER, getPermissionChecker()) + .build(); ConnectedPlayer(VelocityServer server, GameProfile profile, MinecraftConnection connection, @Nullable InetSocketAddress virtualHost, boolean onlineMode) { @@ -1069,6 +1077,11 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { return minecraftOrFmlMessage || knownChannels.contains(message.getChannel()); } + @Override + public @NotNull Pointers pointers() { + return pointers; + } + private class IdentityImpl implements Identity { @Override public @NonNull UUID uuid() { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/console/VelocityConsole.java b/proxy/src/main/java/com/velocitypowered/proxy/console/VelocityConsole.java index 6b465193f..28454816a 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/console/VelocityConsole.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/console/VelocityConsole.java @@ -26,6 +26,8 @@ import com.velocitypowered.api.proxy.ConsoleCommandSource; import com.velocitypowered.proxy.VelocityServer; import java.util.List; import net.kyori.adventure.identity.Identity; +import net.kyori.adventure.permission.PermissionChecker; +import net.kyori.adventure.pointer.Pointers; import net.kyori.adventure.text.Component; import net.kyori.text.TextComponent; import net.kyori.text.format.TextColor; @@ -35,6 +37,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.io.IoBuilder; import org.checkerframework.checker.nullness.qual.NonNull; +import org.jetbrains.annotations.NotNull; import org.jline.reader.Candidate; import org.jline.reader.LineReader; import org.jline.reader.LineReaderBuilder; @@ -45,6 +48,8 @@ public final class VelocityConsole extends SimpleTerminalConsole implements Cons private final VelocityServer server; private PermissionFunction permissionFunction = ALWAYS_TRUE; + private final @NotNull Pointers pointers = ConsoleCommandSource.super.pointers().toBuilder() + .withDynamic(PermissionChecker.POINTER, this::getPermissionChecker).build(); public VelocityConsole(VelocityServer server) { this.server = server; @@ -131,4 +136,9 @@ public final class VelocityConsole extends SimpleTerminalConsole implements Cons protected void shutdown() { this.server.shutdown(true); } + + @Override + public @NotNull Pointers pointers() { + return pointers; + } }