diff --git a/api/src/main/java/com/velocitypowered/api/event/command/CommandExecuteEvent.java b/api/src/main/java/com/velocitypowered/api/event/command/CommandExecuteEvent.java index 65c9b901..7ed704fc 100644 --- a/api/src/main/java/com/velocitypowered/api/event/command/CommandExecuteEvent.java +++ b/api/src/main/java/com/velocitypowered/api/event/command/CommandExecuteEvent.java @@ -26,6 +26,7 @@ public final class CommandExecuteEvent implements ResultedEvent { private final CommandSource commandSource; private final String command; private CommandResult result; + private InvocationInfo invocationInfo; /** * Constructs a CommandExecuteEvent. @@ -34,9 +35,21 @@ public final class CommandExecuteEvent implements ResultedEvent { * @param command the command being executed without first slash */ public CommandExecuteEvent(CommandSource commandSource, String command) { + this(commandSource, command, new InvocationInfo(SignedState.UNSUPPORTED, Source.API)); + } + + /** + * Constructs a CommandExecuteEvent. + * + * @param commandSource the source executing the command + * @param command the command being executed without first slash + * @param invocationInfo the invocation info of this command + */ + public CommandExecuteEvent(CommandSource commandSource, String command, InvocationInfo invocationInfo) { this.commandSource = Preconditions.checkNotNull(commandSource, "commandSource"); this.command = Preconditions.checkNotNull(command, "command"); this.result = CommandResult.allowed(); + this.invocationInfo = invocationInfo; } /** @@ -61,6 +74,16 @@ public final class CommandExecuteEvent implements ResultedEvent { return command; } + /** + * Returns the info of the command invocation. + * + * @since 3.4.0 + * @return invocation info + */ + public InvocationInfo getInvocationInfo() { + return this.invocationInfo; + } + @Override public CommandResult getResult() { return result; @@ -80,6 +103,75 @@ public final class CommandExecuteEvent implements ResultedEvent { + '}'; } + /** + * Represents information about a command invocation, including its signed state and source. + * + * @since 3.4.0 + */ + public record InvocationInfo(SignedState signedState, Source source) { + } + + /** + * Represents the signed state of a command invocation. + * + * @since 3.4.0 + */ + public enum SignedState { + /** + * Indicates that the command was executed from a signed source with signed message arguments, + * This is currently only possible by typing a command in chat with signed arguments. + * + *

Note: Cancelling the {@link CommandExecuteEvent} in this state will result in the player being kicked.

+ * + * @since 3.4.0 + */ + SIGNED_WITH_ARGS, + /** + * Indicates that the command was executed from an signed source with no signed message arguments, + * This is currently only possible by typing a command in chat. + * + * @since 3.4.0 + */ + SIGNED_WITHOUT_ARGS, + /** + * Indicates that the command was executed from an unsigned source, + * such as clicking on a component with a {@link net.kyori.adventure.text.event.ClickEvent.Action#RUN_COMMAND}. + * + *

Clients running version 1.20.5 or later will send this state.

+ * + * @since 3.4.0 + */ + UNSIGNED, + /** + * Indicates that the command invocation does not support signing. + * + *

This state is sent by clients running versions prior to 1.19.3.

+ * + * @since 3.4.0 + */ + UNSUPPORTED + } + + /** + * Represents the source of a command invocation. + * + * @since 3.4.0 + */ + public enum Source { + /** + * Indicates that the command was invoked by a player. + * + * @since 3.4.0 + */ + PLAYER, + /** + * Indicates that the command was invoked programmatically through an API call. + * + * @since 3.4.0 + */ + API + } + /** * Represents the result of the {@link CommandExecuteEvent}. */ diff --git a/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java b/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java index 3b5bbcb8..d1eaa389 100644 --- a/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java +++ b/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java @@ -88,7 +88,8 @@ public enum ProtocolVersion implements Ordered { MINECRAFT_1_20_3(765, "1.20.3", "1.20.4"), MINECRAFT_1_20_5(766, "1.20.5", "1.20.6"), MINECRAFT_1_21(767, "1.21", "1.21.1"), - MINECRAFT_1_21_2(768, "1.21.2", "1.21.3"); + MINECRAFT_1_21_2(768, "1.21.2", "1.21.3"), + MINECRAFT_1_21_4(769, "1.21.4"); private static final int SNAPSHOT_BIT = 30; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java index cecc440c..8b5d4d3d 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java @@ -253,7 +253,7 @@ public class VelocityServer implements ProxyServer, ForwardingAudience { commandManager.metaBuilder(callbackCommand) .plugin(VelocityVirtualPlugin.INSTANCE) .build(), - velocityParentCommand + callbackCommand ); final BrigadierCommand serverCommand = ServerCommand.create(this); commandManager.register( diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java b/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java index f8bb39f0..b4b0e850 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java @@ -218,13 +218,14 @@ public class VelocityCommandManager implements CommandManager { * * @param source the source to execute the command for * @param cmdLine the command to execute + * @param invocationInfo the invocation info * @return the {@link CompletableFuture} of the event */ public CompletableFuture callCommandEvent(final CommandSource source, - final String cmdLine) { + final String cmdLine, final CommandExecuteEvent.InvocationInfo invocationInfo) { Preconditions.checkNotNull(source, "source"); Preconditions.checkNotNull(cmdLine, "cmdLine"); - return eventManager.fire(new CommandExecuteEvent(source, cmdLine)); + return eventManager.fire(new CommandExecuteEvent(source, cmdLine, invocationInfo)); } private boolean executeImmediately0(final CommandSource source, final ParseResults parsed) { @@ -266,7 +267,12 @@ public class VelocityCommandManager implements CommandManager { Preconditions.checkNotNull(source, "source"); Preconditions.checkNotNull(cmdLine, "cmdLine"); - return callCommandEvent(source, cmdLine).thenComposeAsync(event -> { + CommandExecuteEvent.InvocationInfo invocationInfo = new CommandExecuteEvent.InvocationInfo( + CommandExecuteEvent.SignedState.UNSUPPORTED, + CommandExecuteEvent.Source.API + ); + + return callCommandEvent(source, cmdLine, invocationInfo).thenComposeAsync(event -> { CommandExecuteEvent.CommandResult commandResult = event.getResult(); if (commandResult.isForwardToServer() || !commandResult.isAllowed()) { return CompletableFuture.completedFuture(false); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java index 7776e999..e5e02141 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java @@ -38,6 +38,7 @@ import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_20_3; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_20_5; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21_2; +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21_4; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_7_2; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_8; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_9; @@ -359,7 +360,8 @@ public enum StateRegistry { map(0x27, MINECRAFT_1_20_2, false), map(0x28, MINECRAFT_1_20_3, false), map(0x2B, MINECRAFT_1_20_5, false), - map(0x2D, MINECRAFT_1_21_2, false)); + map(0x2D, MINECRAFT_1_21_2, false), + map(0x2F, MINECRAFT_1_21_4, false)); serverbound.register( FinishedUpdatePacket.class, () -> FinishedUpdatePacket.INSTANCE, map(0x0B, MINECRAFT_1_20_2, false), diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/UpsertPlayerInfoPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/UpsertPlayerInfoPacket.java index e2d40d2e..35bf4a96 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/UpsertPlayerInfoPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/UpsertPlayerInfoPacket.java @@ -193,6 +193,11 @@ public class UpsertPlayerInfoPacket implements MinecraftPacket { info.listOrder = ProtocolUtils.readVarInt(buf); }, (version, buf, info) -> { // write ProtocolUtils.writeVarInt(buf, info.listOrder); + }), + UPDATE_HAT((version, buf, info) -> { // read + info.showHat = buf.readBoolean(); + }, (version, buf, info) -> { // write + buf.writeBoolean(info.showHat); }); private final Read read; @@ -223,6 +228,7 @@ public class UpsertPlayerInfoPacket implements MinecraftPacket { private int gameMode; @Nullable private ComponentHolder displayName; + private boolean showHat; private int listOrder; @Nullable private RemoteChatSession chatSession; @@ -256,6 +262,10 @@ public class UpsertPlayerInfoPacket implements MinecraftPacket { return displayName; } + public boolean isShowHat() { + return showHat; + } + public int getListOrder() { return listOrder; } @@ -285,6 +295,10 @@ public class UpsertPlayerInfoPacket implements MinecraftPacket { this.displayName = displayName; } + public void setShowHat(boolean showHat) { + this.showHat = showHat; + } + public void setListOrder(int listOrder) { this.listOrder = listOrder; } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/CommandHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/CommandHandler.java index 9786fe14..8e39d78a 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/CommandHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/CommandHandler.java @@ -56,8 +56,10 @@ public interface CommandHandler { default void queueCommandResult(VelocityServer server, ConnectedPlayer player, BiFunction> futurePacketCreator, - String message, Instant timestamp, @Nullable LastSeenMessages lastSeenMessages) { - CompletableFuture eventFuture = server.getCommandManager().callCommandEvent(player, message); + String message, Instant timestamp, @Nullable LastSeenMessages lastSeenMessages, + CommandExecuteEvent.InvocationInfo invocationInfo) { + CompletableFuture eventFuture = server.getCommandManager().callCommandEvent(player, message, + invocationInfo); player.getChatQueue().queuePacket( newLastSeenMessages -> eventFuture .thenComposeAsync(event -> futurePacketCreator.apply(event, newLastSeenMessages)) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/keyed/KeyedCommandHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/keyed/KeyedCommandHandler.java index 1d3751e4..5baedfb4 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/keyed/KeyedCommandHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/keyed/KeyedCommandHandler.java @@ -111,6 +111,6 @@ public class KeyedCommandHandler implements CommandHandler { } return null; }); - }, command, Instant.now(), null); + }, command, Instant.now(), null, new CommandExecuteEvent.InvocationInfo(CommandExecuteEvent.SignedState.UNSUPPORTED, CommandExecuteEvent.Source.PLAYER)); } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/session/SessionCommandHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/session/SessionCommandHandler.java index 0e47feed..8d1dc0f2 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/session/SessionCommandHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/session/SessionCommandHandler.java @@ -117,6 +117,7 @@ public class SessionCommandHandler implements CommandHandler