diff --git a/patches/api/0364-API-for-creating-command-sender-which-forwards-feedb.patch b/patches/api/0364-API-for-creating-command-sender-which-forwards-feedb.patch new file mode 100644 index 0000000000..ec483fda77 --- /dev/null +++ b/patches/api/0364-API-for-creating-command-sender-which-forwards-feedb.patch @@ -0,0 +1,54 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jason Penilla <11360596+jpenilla@users.noreply.github.com> +Date: Tue, 1 Feb 2022 15:51:44 -0700 +Subject: [PATCH] API for creating command sender which forwards feedback + + +diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java +index 53c4e5ca208ee17c7c244e416c537c7b63edf194..3a8326735b62521f8fb95c51a0909d8b7bac83d1 100644 +--- a/src/main/java/org/bukkit/Bukkit.java ++++ b/src/main/java/org/bukkit/Bukkit.java +@@ -1297,6 +1297,20 @@ public final class Bukkit { + return server.getConsoleSender(); + } + ++ // Paper start ++ /** ++ * Creates a special {@link CommandSender} which redirects command feedback (in the form of chat messages) to the ++ * specified listener. The returned sender will have the same effective permissions as {@link #getConsoleSender()}. ++ * ++ * @param feedback feedback listener ++ * @return a command sender ++ */ ++ @NotNull ++ public static CommandSender createCommandSender(final @NotNull java.util.function.Consumer feedback) { ++ return server.createCommandSender(feedback); ++ } ++ // Paper end ++ + /** + * Gets the folder that contains all of the various {@link World}s. + * +diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java +index e48af3822e9f118399c3a1c9358c56efae12e0da..73446141e8c50b71a17ff6f0c528a62d5c75751b 100644 +--- a/src/main/java/org/bukkit/Server.java ++++ b/src/main/java/org/bukkit/Server.java +@@ -1078,6 +1078,18 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi + @NotNull + public ConsoleCommandSender getConsoleSender(); + ++ // Paper start ++ /** ++ * Creates a special {@link CommandSender} which redirects command feedback (in the form of chat messages) to the ++ * specified listener. The returned sender will have the same effective permissions as {@link #getConsoleSender()}. ++ * ++ * @param feedback feedback listener ++ * @return a command sender ++ */ ++ @NotNull ++ public CommandSender createCommandSender(final @NotNull java.util.function.Consumer feedback); ++ // Paper end ++ + /** + * Gets the folder that contains all of the various {@link World}s. + * diff --git a/patches/server/0861-API-for-creating-command-sender-which-forwards-feedb.patch b/patches/server/0861-API-for-creating-command-sender-which-forwards-feedb.patch new file mode 100644 index 0000000000..f7a24cdaea --- /dev/null +++ b/patches/server/0861-API-for-creating-command-sender-which-forwards-feedb.patch @@ -0,0 +1,179 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jason Penilla <11360596+jpenilla@users.noreply.github.com> +Date: Tue, 1 Feb 2022 15:51:55 -0700 +Subject: [PATCH] API for creating command sender which forwards feedback + + +diff --git a/src/main/java/io/papermc/paper/commands/FeedbackForwardingSender.java b/src/main/java/io/papermc/paper/commands/FeedbackForwardingSender.java +new file mode 100644 +index 0000000000000000000000000000000000000000..f7c86155ce0cfd9b4bf8a2b79d77a656d795c95f +--- /dev/null ++++ b/src/main/java/io/papermc/paper/commands/FeedbackForwardingSender.java +@@ -0,0 +1,112 @@ ++package io.papermc.paper.commands; ++ ++import io.papermc.paper.adventure.PaperAdventure; ++import io.papermc.paper.text.PaperComponents; ++import java.util.UUID; ++import java.util.function.Consumer; ++import net.kyori.adventure.audience.MessageType; ++import net.kyori.adventure.identity.Identity; ++import net.kyori.adventure.text.Component; ++import net.minecraft.commands.CommandSource; ++import net.minecraft.commands.CommandSourceStack; ++import net.minecraft.network.chat.TextComponent; ++import net.minecraft.server.level.ServerLevel; ++import net.minecraft.world.phys.Vec2; ++import net.minecraft.world.phys.Vec3; ++import org.bukkit.command.CommandSender; ++import org.bukkit.craftbukkit.CraftServer; ++import org.bukkit.craftbukkit.command.ServerCommandSender; ++import org.checkerframework.checker.nullness.qual.NonNull; ++import org.checkerframework.checker.nullness.qual.Nullable; ++import org.checkerframework.framework.qual.DefaultQualifier; ++ ++@DefaultQualifier(NonNull.class) ++public final class FeedbackForwardingSender extends ServerCommandSender { ++ private final Consumer feedback; ++ private final CraftServer server; ++ ++ public FeedbackForwardingSender(final Consumer feedback, final CraftServer server) { ++ super(((ServerCommandSender) server.getConsoleSender()).perm); ++ this.server = server; ++ this.feedback = feedback; ++ } ++ ++ @Override ++ public void sendMessage(final String message) { ++ this.sendMessage(PaperComponents.legacySectionSerializer().deserialize(message)); ++ } ++ ++ @Override ++ public void sendMessage(final String... messages) { ++ for (final String message : messages) { ++ this.sendMessage(message); ++ } ++ } ++ ++ @Override ++ public void sendMessage(final Identity identity, final Component message, final MessageType type) { ++ this.feedback.accept(message); ++ } ++ ++ @Override ++ public String getName() { ++ return "FeedbackForwardingSender"; ++ } ++ ++ @Override ++ public Component name() { ++ return Component.text(this.getName()); ++ } ++ ++ @Override ++ public boolean isOp() { ++ return true; ++ } ++ ++ @Override ++ public void setOp(final boolean value) { ++ throw new UnsupportedOperationException("Cannot change operator status of " + this.getClass().getName()); ++ } ++ ++ public CommandSourceStack asVanilla() { ++ final @Nullable ServerLevel overworld = this.server.getServer().overworld(); ++ return new CommandSourceStack( ++ new Source(this), ++ overworld == null ? Vec3.ZERO : Vec3.atLowerCornerOf(overworld.getSharedSpawnPos()), ++ Vec2.ZERO, ++ overworld, ++ 4, ++ this.getName(), ++ new TextComponent(this.getName()), ++ this.server.getServer(), ++ null ++ ); ++ } ++ ++ private record Source(FeedbackForwardingSender sender) implements CommandSource { ++ @Override ++ public void sendMessage(final net.minecraft.network.chat.Component message, final UUID sender) { ++ this.sender.sendMessage(Identity.identity(sender), PaperAdventure.asAdventure(message)); ++ } ++ ++ @Override ++ public boolean acceptsSuccess() { ++ return true; ++ } ++ ++ @Override ++ public boolean acceptsFailure() { ++ return true; ++ } ++ ++ @Override ++ public boolean shouldInformAdmins() { ++ return false; ++ } ++ ++ @Override ++ public CommandSender getBukkitSender(final CommandSourceStack stack) { ++ return this.sender; ++ } ++ } ++} +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +index 54e8f0f367645f3aa8af5b1cb69c39c0cec9381f..1a1f5c8f9a049d65043f12374fe694068f7d08cf 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +@@ -1988,6 +1988,13 @@ public final class CraftServer implements Server { + return console.console; + } + ++ // Paper start ++ @Override ++ public CommandSender createCommandSender(final java.util.function.Consumer feedback) { ++ return new io.papermc.paper.commands.FeedbackForwardingSender(feedback, this); ++ } ++ // Paper end ++ + public EntityMetadataStore getEntityMetadata() { + return this.entityMetadata; + } +diff --git a/src/main/java/org/bukkit/craftbukkit/command/ServerCommandSender.java b/src/main/java/org/bukkit/craftbukkit/command/ServerCommandSender.java +index eaff8df6c8c12c64e005a68a02e2e35ed88f757c..1c638a4b1f2c841928d8b2a7ae43e4ebb1f7eac7 100644 +--- a/src/main/java/org/bukkit/craftbukkit/command/ServerCommandSender.java ++++ b/src/main/java/org/bukkit/craftbukkit/command/ServerCommandSender.java +@@ -13,9 +13,15 @@ import org.bukkit.plugin.Plugin; + + public abstract class ServerCommandSender implements CommandSender { + private static PermissibleBase blockPermInst; +- private final PermissibleBase perm; ++ public final PermissibleBase perm; // Paper + private net.kyori.adventure.pointer.Pointers adventure$pointers; // Paper - implement pointers + ++ // Paper start ++ public ServerCommandSender(final PermissibleBase permissibleBase) { ++ this.perm = permissibleBase; ++ } ++ // Paper end ++ + public ServerCommandSender() { + if (this instanceof CraftBlockCommandSender) { + if (ServerCommandSender.blockPermInst == null) { +diff --git a/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java b/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java +index 4aa1dc543950b5de64345b3403a6d0bc41c521df..4525fb3bc9b137bce3b59310a8aecca96d6ad5ba 100644 +--- a/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java ++++ b/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java +@@ -84,6 +84,11 @@ public final class VanillaCommandWrapper extends BukkitCommand { + if (sender instanceof ProxiedCommandSender) { + return ((ProxiedNativeCommandSender) sender).getHandle(); + } ++ // Paper start ++ if (sender instanceof io.papermc.paper.commands.FeedbackForwardingSender feedback) { ++ return feedback.asVanilla(); ++ } ++ // Paper end + + throw new IllegalArgumentException("Cannot make " + sender + " a vanilla command listener"); + }