Skip to content
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
import com.mojang.brigadier.RedirectModifier;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.tree.CommandNode;
import net.kyori.adventure.text.ComponentLike;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import org.bukkit.GameRules;
import org.bukkit.Location;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Entity;
Expand Down Expand Up @@ -81,4 +85,119 @@ 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 sendReply(ComponentLike message);

/**
* Sends a system message with the MiniMessage format to the {@link #getExecutor()}
* if it is a {@link Player}, otherwise sends it to the {@link #getSender()}.
*
* <p>See <a href="https://docs.advntr.dev/minimessage/">MiniMessage docs</a> and
Comment thread
Strokkur424 marked this conversation as resolved.
Outdated
* <a href="https://docs.advntr.dev/minimessage/dynamic-replacements">MiniMessage Placeholders docs</a>
* for more information on the format.</p>
*
* @param message the MiniMessage message to send
* @param resolvers resolvers to use
*/
default void sendRichReply(String message, TagResolver... resolvers) {
sendReply(MiniMessage.miniMessage().deserialize(message, resolvers));
}

/**
* Sends a system message to the {@link #getSender()}, admins, and console indicating successful command execution
* according to vanilla semantics.
*
* <p>This currently includes checking for environments with suppressed output,
* {@link GameRules#SEND_COMMAND_FEEDBACK}, and {@link GameRules#LOG_ADMIN_COMMANDS}.</p>
*
* @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 to the {@link #getSender()}, admins, and console indicating successful command execution
* according to vanilla semantics. This method informs admins and the console of this success.
*
* <p>This currently includes checking for environments with suppressed output,
* {@link GameRules#SEND_COMMAND_FEEDBACK}, and {@link GameRules#LOG_ADMIN_COMMANDS}.</p>
*
* @param message the message to send
* @see #sendSuccess(ComponentLike, boolean) to disable if admins and console should be informed
*/
default void sendSuccess(ComponentLike message) {
sendSuccess(message, true);
Comment thread
Strokkur424 marked this conversation as resolved.
Outdated
}

/**
* Sends a system message with the MiniMessage format to the {@link #getSender()}, admins, and console indicating
* successful command execution according to vanilla semantics.
*
* <p>This currently includes checking for environments with suppressed output,
* {@link GameRules#SEND_COMMAND_FEEDBACK}, and {@link GameRules#LOG_ADMIN_COMMANDS}.</p>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can extend a little this? (where this is mentioned)

maybe can be to much info but the logic for admins and console... its like

  • Admins
    • The gamerule for command feedback
    • minecraft.admin.command_feedback permission (or op)
  • Console
    • The gamerule for log admin commands
    • The silentCommandBlocks when source its not a comand block

but maybe its to much info for this... and maybe can be just a mention in the docs rather than here (?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've spent some time thinking about how one could properly word this, and I have come to the evaluation that I think the current paragraph for this is already good enough. Generally, I think it's logical that command feedback is only sent for players with the relevant permission (the docs have that permission documented, albeit the description is outdated).

*
* <p>See <a href="https://docs.advntr.dev/minimessage/">MiniMessage docs</a> and
* <a href="https://docs.advntr.dev/minimessage/dynamic-replacements">MiniMessage Placeholders docs</a>
* for more information on the format.</p>
*
* @param message the MiniMessage message to send
* @param allowInformingAdmins whether admins and console may be informed of this success
* @param resolvers resolvers to use
*/
default void sendRichSuccess(String message, boolean allowInformingAdmins, TagResolver... resolvers) {
Comment thread
Strokkur424 marked this conversation as resolved.
Outdated
sendSuccess(MiniMessage.miniMessage().deserialize(message, getSender(), resolvers), allowInformingAdmins);
}

/**
* Sends a system message with the MiniMessage format to the {@link #getSender()}, admins, and console indicating
* successful command execution according to vanilla semantics. This method informs admins and the console of
* this success.
*
* <p>This currently includes checking for environments with suppressed output,
* {@link GameRules#SEND_COMMAND_FEEDBACK}, and {@link GameRules#LOG_ADMIN_COMMANDS}.</p>
*
* <p>See <a href="https://docs.advntr.dev/minimessage/">MiniMessage docs</a> and
* <a href="https://docs.advntr.dev/minimessage/dynamic-replacements">MiniMessage Placeholders docs</a>
* for more information on the format.</p>
*
* @param message the MiniMessage message to send
* @param resolvers resolvers to use
* @see #sendSuccess(ComponentLike, boolean) to disable if admins and console should be informed
*/
default void sendRichSuccess(String message, TagResolver... resolvers) {
sendRichSuccess(message, true, resolvers);
}

/**
* 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.
*
* <p>Respects vanilla semantics for accepting failure output and suppressed output environments.</p>
*
* @param message the message to send
*/
void sendFailure(ComponentLike message);

/**
* Sends a system message with the MiniMessage format 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.
*
* <p>Respects vanilla semantics for accepting failure output and suppressed output environments.</p>
*
* <p>See <a href="https://docs.advntr.dev/minimessage/">MiniMessage docs</a> and
* <a href="https://docs.advntr.dev/minimessage/dynamic-replacements">MiniMessage Placeholders docs</a>
* for more information on the format.</p>
*
* @param message the MiniMessage message to send
* @param resolvers resolvers to use
*/
default void sendRichFailure(String message, TagResolver... resolvers) {
sendFailure(MiniMessage.miniMessage().deserialize(message, getSender(), resolvers));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec2;
Expand Down Expand Up @@ -49,6 +51,24 @@ default CommandSourceStack withExecutor(Entity executor) {
return this.getHandle().withEntity(((CraftEntity) executor).getHandle());
}

@Override
default void sendReply(final ComponentLike message) {
Preconditions.checkNotNull(message, "message cannot be null.");
Comment thread
Strokkur424 marked this conversation as resolved.
Outdated
this.getHandle().sendSystemMessage(PaperAdventure.asVanilla(message.asComponent()));
}

@Override
default void sendSuccess(final ComponentLike message, final boolean allowInformingAdmins) {
Preconditions.checkNotNull(message, "message cannot be null.");
this.getHandle().sendSuccess(() -> PaperAdventure.asVanilla(message.asComponent()), allowInformingAdmins);
}

@Override
default void sendFailure(final ComponentLike message) {
Preconditions.checkNotNull(message, "message cannot be null.");
this.getHandle().sendFailure(PaperAdventure.asVanilla(message.asComponent()), false);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well this comes from the base PR but not sure about ignore the default style used in vanilla, but its more a opinion... most of people who use this wanna custom messages so...

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could theoretically just add an overload that does apply the style too. Maybe sendFailureStyled? Alternatively, could be a method parameter. Idk, whatever you think is best?

}

@Override
default Player getPlayerOrThrow() throws CommandSyntaxException {
return this.getHandle().getPlayerOrException().getBukkitEntity();
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
Expand All @@ -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.translatable(ctx.getArgument("name", Enchantment.class)), false);
return Command.SINGLE_SUCCESS;
})
).build()
Expand All @@ -55,7 +59,7 @@ private static void registerViaLifecycleEvents(final JavaPlugin plugin) {
Commands.argument("key", ArgumentTypes.resourceKey(RegistryKey.ENCHANTMENT))
.executes(ctx -> {
final TypedKey<Enchantment> key = RegistryArgumentExtractor.getTypedKey(ctx, RegistryKey.ENCHANTMENT, "key");
ctx.getSource().getSender().sendPlainMessage(key.toString());
ctx.getSource().sendSuccess(Component.text(key.toString()), false);
return Command.SINGLE_SUCCESS;
})
).build()
Expand All @@ -65,15 +69,15 @@ private static void registerViaLifecycleEvents(final JavaPlugin plugin) {
Commands.argument("pos", ArgumentTypes.finePosition(false))
.executes(ctx -> {
final FinePositionResolver position = ctx.getArgument("pos", FinePositionResolver.class);
ctx.getSource().getSender().sendPlainMessage("Position: " + position.resolve(ctx.getSource()));
ctx.getSource().sendRichSuccess("Position: <red>" + position.resolve(ctx.getSource()), false);
return Command.SINGLE_SUCCESS;
})
).build()
);
// ensure plugin commands override
commands.register(Commands.literal("tag")
.executes(ctx -> {
ctx.getSource().getSender().sendPlainMessage("overriden command");
ctx.getSource().sendRichSuccess("overriden command", false);
return Command.SINGLE_SUCCESS;
})
.build(),
Expand All @@ -88,7 +92,7 @@ private static void registerViaLifecycleEvents(final JavaPlugin plugin) {
.then(Commands.literal("sub_command")
.requires(source -> source.getSender().hasPermission("testplugin.test"))
.executes(ctx -> {
ctx.getSource().getSender().sendPlainMessage("root_command sub_command");
ctx.getSource().sendRichSuccess("<red>root_command <gold>sub_command", false);
return Command.SINGLE_SUCCESS;
})).build(),
null,
Expand Down Expand Up @@ -126,7 +130,7 @@ public void execute(@NotNull final CommandSourceStack commandSourceStack, final
return true;
})
.executes(ctx -> {
ctx.getSource().getExecutor().sendMessage("You are in lava!");
ctx.getSource().sendRichReply("You are in lava!");
return Command.SINGLE_SUCCESS;
})).build(),
null,
Expand Down Expand Up @@ -165,14 +169,14 @@ public static void registerViaBootstrap(final BootstrapContext context) {
.then(Commands.literal("item")
.then(Commands.argument("mat", MaterialArgumentType.item())
.executes(ctx -> {
ctx.getSource().getSender().sendPlainMessage(ctx.getArgument("mat", Material.class).name());
ctx.getSource().sendSuccess(Component.translatable(ctx.getArgument("mat", Material.class)), false);
return Command.SINGLE_SUCCESS;
})
)
).then(Commands.literal("block")
.then(Commands.argument("mat", MaterialArgumentType.block())
.executes(ctx -> {
ctx.getSource().getSender().sendPlainMessage(ctx.getArgument("mat", Material.class).name());
ctx.getSource().sendSuccess(Component.translatable(ctx.getArgument("mat", Material.class)), false);
return Command.SINGLE_SUCCESS;
})
)
Expand All @@ -181,15 +185,30 @@ public static void registerViaBootstrap(final BootstrapContext context) {
null,
Collections.emptyList()
);

commands.register(Commands.literal("send_success")
.then(Commands.argument("allow_inform_admins", BoolArgumentType.bool())
.then(Commands.argument("msg", StringArgumentType.greedyString())
.executes(ctx -> {
ctx.getSource().sendRichSuccess(
StringArgumentType.getString(ctx, "msg"),
BoolArgumentType.getBool(ctx, "allow_inform_admins")
);
return Command.SINGLE_SUCCESS;
})))
.build(),
null,
Collections.emptyList()
);
});

lifecycleManager.registerEventHandler(LifecycleEvents.COMMANDS.newHandler(event -> {
final Commands commands = event.registrar();
commands.register(Commands.literal("heya")
.then(Commands.argument("range", ArgumentTypes.doubleRange())
.executes((ct) -> {
ct.getSource().getSender().sendPlainMessage(ct.getArgument("range", DoubleRangeProvider.class).range().toString());
return 1;
ct.getSource().sendSuccess(Component.text(ct.getArgument("range", DoubleRangeProvider.class).range().toString()), false);
return Command.SINGLE_SUCCESS;
})
).build(),
null,
Expand Down
Loading