diff --git a/paper-api/src/main/java/io/papermc/paper/command/brigadier/CommandSourceStack.java b/paper-api/src/main/java/io/papermc/paper/command/brigadier/CommandSourceStack.java index a6e01ffa3f30..1a6c78892a0e 100644 --- a/paper-api/src/main/java/io/papermc/paper/command/brigadier/CommandSourceStack.java +++ b/paper-api/src/main/java/io/papermc/paper/command/brigadier/CommandSourceStack.java @@ -2,9 +2,12 @@ import com.mojang.brigadier.RedirectModifier; import com.mojang.brigadier.tree.CommandNode; +import net.kyori.adventure.text.ComponentLike; +import org.bukkit.GameRule; import org.bukkit.Location; import org.bukkit.command.CommandSender; import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; import org.jetbrains.annotations.ApiStatus; import org.jspecify.annotations.Nullable; @@ -67,4 +70,34 @@ public interface CommandSourceStack { * @see com.mojang.brigadier.builder.ArgumentBuilder#fork(CommandNode, RedirectModifier) */ CommandSourceStack withExecutor(Entity executor); + + /** + * Sends a system message to the {@link #getExecutor()} if it is a {@link Player}, + * otherwise sends a system message to the {@link #getSender()}. + * + * @param message the message to send + */ + void sendToTarget(ComponentLike message); + + /** + * Sends a system message to the {@link #getSender()}, admins, and console indicating successful command execution + * according to vanilla semantics. + * + *
This currently includes checking for environments with suppressed output, + * {@link GameRule#SEND_COMMAND_FEEDBACK}, and {@link GameRule#LOG_ADMIN_COMMANDS}.
+ * + * @param message the message to send + * @param allowInformingAdmins whether admins and console may be informed of this success + */ + void sendSuccess(ComponentLike message, boolean allowInformingAdmins); + + /** + * Sends a system message indicating a failed command execution to the {@link #getSender()}. + * Does not apply red styling to the message as vanilla does to allow for custom failure message styling. + * + *Respects vanilla semantics for accepting failure output and suppressed output environments.
+ * + * @param message the message to send + */ + void sendFailure(ComponentLike message); } diff --git a/paper-server/src/main/java/io/papermc/paper/command/brigadier/PaperCommandSourceStack.java b/paper-server/src/main/java/io/papermc/paper/command/brigadier/PaperCommandSourceStack.java index 0f7bbc9193bd..330919d7e50f 100644 --- a/paper-server/src/main/java/io/papermc/paper/command/brigadier/PaperCommandSourceStack.java +++ b/paper-server/src/main/java/io/papermc/paper/command/brigadier/PaperCommandSourceStack.java @@ -2,6 +2,8 @@ import com.destroystokyo.paper.brigadier.BukkitBrigadierCommandSource; import com.google.common.base.Preconditions; +import io.papermc.paper.adventure.PaperAdventure; +import net.kyori.adventure.text.ComponentLike; import net.minecraft.world.level.Level; import net.minecraft.world.phys.Vec2; import net.minecraft.world.phys.Vec3; @@ -12,6 +14,8 @@ import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; +import static java.util.Objects.requireNonNull; + public interface PaperCommandSourceStack extends CommandSourceStack, BukkitBrigadierCommandSource { net.minecraft.commands.CommandSourceStack getHandle(); @@ -26,14 +30,12 @@ public interface PaperCommandSourceStack extends CommandSourceStack, BukkitBriga } @Override - @NonNull - default CommandSender getSender() { + default @NonNull CommandSender getSender() { return this.getHandle().getBukkitSender(); } @Override - @Nullable - default Entity getExecutor() { + default @Nullable Entity getExecutor() { net.minecraft.world.entity.Entity nmsEntity = this.getHandle().getEntity(); if (nmsEntity == null) { return null; @@ -43,11 +45,29 @@ default Entity getExecutor() { } @Override - default CommandSourceStack withExecutor(@NonNull Entity executor) { + default @NonNull CommandSourceStack withExecutor(@NonNull Entity executor) { Preconditions.checkNotNull(executor, "Executor cannot be null."); return this.getHandle().withEntity(((CraftEntity) executor).getHandle()); } + @Override + default void sendToTarget(final @NonNull ComponentLike message) { + requireNonNull(message, "message"); + this.getHandle().sendSystemMessage(PaperAdventure.asVanilla(message.asComponent())); + } + + @Override + default void sendSuccess(final @NonNull ComponentLike message, final boolean allowInformingAdmins) { + requireNonNull(message, "message"); + this.getHandle().sendSuccess(() -> PaperAdventure.asVanilla(message.asComponent()), allowInformingAdmins); + } + + @Override + default void sendFailure(final @NonNull ComponentLike message) { + requireNonNull(message, "message"); + this.getHandle().sendFailure(PaperAdventure.asVanilla(message.asComponent()), false); + } + // OLD METHODS @Override default org.bukkit.entity.Entity getBukkitEntity() { diff --git a/test-plugin/src/main/java/io/papermc/testplugin/brigtests/Registration.java b/test-plugin/src/main/java/io/papermc/testplugin/brigtests/Registration.java index a45dcfc1512e..9572e2d6af36 100644 --- a/test-plugin/src/main/java/io/papermc/testplugin/brigtests/Registration.java +++ b/test-plugin/src/main/java/io/papermc/testplugin/brigtests/Registration.java @@ -1,6 +1,8 @@ package io.papermc.testplugin.brigtests; import com.mojang.brigadier.Command; +import com.mojang.brigadier.arguments.BoolArgumentType; +import com.mojang.brigadier.arguments.StringArgumentType; import io.papermc.paper.command.brigadier.BasicCommand; import io.papermc.paper.command.brigadier.CommandSourceStack; import io.papermc.paper.command.brigadier.Commands; @@ -19,6 +21,8 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; import org.bukkit.Material; import org.bukkit.command.CommandSender; import org.bukkit.command.defaults.BukkitCommand; @@ -45,7 +49,7 @@ private static void registerViaLifecycleEvents(final JavaPlugin plugin) { .then( Commands.argument("name", ArgumentTypes.resource(RegistryKey.ENCHANTMENT)) .executes(ctx -> { - ctx.getSource().getSender().sendPlainMessage(ctx.getArgument("name", Enchantment.class).toString()); + ctx.getSource().sendSuccess(Component.text(ctx.getArgument("name", Enchantment.class).toString()), false); return Command.SINGLE_SUCCESS; }) ).build() @@ -55,7 +59,7 @@ private static void registerViaLifecycleEvents(final JavaPlugin plugin) { Commands.argument("key", ArgumentTypes.resourceKey(RegistryKey.ENCHANTMENT)) .executes(ctx -> { final TypedKey