Backport "Fix command signs" to 1.16.5 (#6169)

This commit is contained in:
LemonCaramel 2021-07-13 13:33:06 +09:00 committed by GitHub
parent cfbab5ca9f
commit 4615f58a70
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 204 additions and 40 deletions

View File

@ -0,0 +1,46 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Fri, 9 Jul 2021 17:44:33 -0700
Subject: [PATCH] Add PlayerSignCommandPreprocessEvent
diff --git a/src/main/java/io/papermc/paper/event/player/PlayerSignCommandPreprocessEvent.java b/src/main/java/io/papermc/paper/event/player/PlayerSignCommandPreprocessEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..a51a2288bf812e7d8845a6ec70274d625ff793b6
--- /dev/null
+++ b/src/main/java/io/papermc/paper/event/player/PlayerSignCommandPreprocessEvent.java
@@ -0,0 +1,34 @@
+package io.papermc.paper.event.player;
+
+import org.bukkit.block.Sign;
+import org.bukkit.entity.Player;
+import org.bukkit.event.player.PlayerCommandPreprocessEvent;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Set;
+
+/**
+ * Called when a {@link Player} clicks a sign that causes a command to run.
+ * <p>
+ * This command is run with elevated permissions which allows players to access commands on signs they wouldn't
+ * normally be able to run.
+ */
+public class PlayerSignCommandPreprocessEvent extends PlayerCommandPreprocessEvent {
+
+ private final Sign sign;
+
+ public PlayerSignCommandPreprocessEvent(@NotNull Player player, @NotNull String message, @NotNull Set<Player> recipients, @NotNull Sign sign) {
+ super(player, message, recipients);
+ this.sign = sign;
+ }
+
+ /**
+ * Gets the sign that the command originated from.
+ *
+ * @return the sign
+ */
+ @NotNull
+ public Sign getSign() {
+ return sign;
+ }
+}

View File

@ -0,0 +1,158 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Fri, 9 Jul 2021 13:50:48 -0700
Subject: [PATCH] Fix commands from signs not firing command events
This patch changes sign command logic so that `run_command` click events:
- are logged to the console
- fire PlayerCommandPreprocessEvent
- work with double-slash commands like `//wand`
- sends failure messages to the player who clicked the sign
diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
index 32ac923d41d89ae170924532245bde9975bcfbd3..dc4bd421ea36779342a35e82830a05fa68b96f7b 100644
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
@@ -845,4 +845,9 @@ public class PaperWorldConfig {
private void fixInvulnerableEndCrystalExploit() {
fixInvulnerableEndCrystalExploit = getBoolean("unsupported-settings.fix-invulnerable-end-crystal-exploit", fixInvulnerableEndCrystalExploit);
}
+
+ public boolean showSignClickCommandFailureMessagesToPlayer = false;
+ private void showSignClickCommandFailureMessagesToPlayer() {
+ showSignClickCommandFailureMessagesToPlayer = getBoolean("show-sign-click-command-failure-msgs-to-player", showSignClickCommandFailureMessagesToPlayer);
+ }
}
diff --git a/src/main/java/io/papermc/paper/commands/DelegatingCommandSource.java b/src/main/java/io/papermc/paper/commands/DelegatingCommandSource.java
new file mode 100644
index 0000000000000000000000000000000000000000..5c3f17163609ee81f820e4f496017d507578daf3
--- /dev/null
+++ b/src/main/java/io/papermc/paper/commands/DelegatingCommandSource.java
@@ -0,0 +1,42 @@
+package io.papermc.paper.commands;
+
+import net.minecraft.commands.CommandListenerWrapper;
+import net.minecraft.commands.ICommandListener;
+import net.minecraft.network.chat.IChatBaseComponent;
+import org.bukkit.command.CommandSender;
+
+import java.util.UUID;
+
+public class DelegatingCommandSource implements ICommandListener {
+
+ private final ICommandListener delegate;
+
+ public DelegatingCommandSource(ICommandListener delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public void sendMessage(IChatBaseComponent message, UUID sender) {
+ delegate.sendMessage(message, sender);
+ }
+
+ @Override
+ public boolean shouldSendSuccess() {
+ return delegate.shouldSendSuccess();
+ }
+
+ @Override
+ public boolean shouldSendFailure() {
+ return delegate.shouldSendFailure();
+ }
+
+ @Override
+ public boolean shouldBroadcastCommands() {
+ return delegate.shouldBroadcastCommands();
+ }
+
+ @Override
+ public CommandSender getBukkitSender(CommandListenerWrapper wrapper) {
+ return delegate.getBukkitSender(wrapper);
+ }
+}
diff --git a/src/main/java/net/minecraft/commands/CommandDispatcher.java b/src/main/java/net/minecraft/commands/CommandDispatcher.java
index 7e30ec9a08d919d2ae9218ee0a11f77719129f07..4270a9bbc272706b5a88807d465a32e73d18b90f 100644
--- a/src/main/java/net/minecraft/commands/CommandDispatcher.java
+++ b/src/main/java/net/minecraft/commands/CommandDispatcher.java
@@ -245,6 +245,7 @@ public class CommandDispatcher {
return this.a(sender, newCommand, newCommand, false);
}
+ public int performCommand(CommandListenerWrapper commandlistenerwrapper, String s) { return this.a(commandlistenerwrapper, s); } // Paper - OBFHELPER
public int a(CommandListenerWrapper commandlistenerwrapper, String s) {
return this.a(commandlistenerwrapper, s, s, true);
}
diff --git a/src/main/java/net/minecraft/world/level/block/entity/TileEntitySign.java b/src/main/java/net/minecraft/world/level/block/entity/TileEntitySign.java
index 7f78f388584899b13ff983f0dc37c679bfb1507e..1fc3e59551e26b25ba367b45df6024107450a444 100644
--- a/src/main/java/net/minecraft/world/level/block/entity/TileEntitySign.java
+++ b/src/main/java/net/minecraft/world/level/block/entity/TileEntitySign.java
@@ -32,6 +32,7 @@ public class TileEntitySign extends TileEntity implements ICommandListener { //
private EnumColor color;
public java.util.UUID signEditor; // Paper
private static final boolean CONVERT_LEGACY_SIGNS = Boolean.getBoolean("convertLegacySigns"); // Paper
+ private static final org.apache.logging.log4j.Logger LOGGER = org.apache.logging.log4j.LogManager.getLogger(); // Paper
public TileEntitySign() {
super(TileEntityTypes.SIGN);
@@ -155,7 +156,18 @@ public class TileEntitySign extends TileEntity implements ICommandListener { //
ChatClickable chatclickable = chatmodifier.getClickEvent();
if (chatclickable.a() == ChatClickable.EnumClickAction.RUN_COMMAND) {
- entityhuman.getMinecraftServer().getCommandDispatcher().a(this.a((EntityPlayer) entityhuman), chatclickable.b());
+ // Paper start
+ EntityPlayer player = (EntityPlayer) entityhuman;
+ String command = chatclickable.b().startsWith("/") ? chatclickable.b() : "/" + chatclickable.b();
+ if (org.spigotmc.SpigotConfig.logCommands) {
+ LOGGER.info("{} issued server command: {}", entityhuman.getName(), command);
+ }
+ io.papermc.paper.event.player.PlayerSignCommandPreprocessEvent event = new io.papermc.paper.event.player.PlayerSignCommandPreprocessEvent(player.getBukkitEntity(), command, new org.bukkit.craftbukkit.util.LazyPlayerSet(player.server), (org.bukkit.block.Sign) net.minecraft.server.MCUtil.toBukkitBlock(this.world, this.position).getState());
+ if (!event.callEvent()) {
+ return false;
+ }
+ player.server.getCommandDispatcher().performCommand(this.createCommandSourceStack(((org.bukkit.craftbukkit.entity.CraftPlayer) event.getPlayer()).getHandle()), event.getMessage());
+ // Paper end
}
}
}
@@ -188,12 +200,26 @@ public class TileEntitySign extends TileEntity implements ICommandListener { //
}
// CraftBukkit end
+ public CommandListenerWrapper createCommandSourceStack(@Nullable EntityPlayer entityplayer) { return this.a(entityplayer); } // Paper - OBFHELPER
public CommandListenerWrapper a(@Nullable EntityPlayer entityplayer) {
String s = entityplayer == null ? "Sign" : entityplayer.getDisplayName().getString();
Object object = entityplayer == null ? new ChatComponentText("Sign") : entityplayer.getScoreboardDisplayName();
+ // Paper start - send messages back to the player
+ ICommandListener commandSource = this.world.paperConfig.showSignClickCommandFailureMessagesToPlayer ? new io.papermc.paper.commands.DelegatingCommandSource(this) {
+ @Override
+ public void sendMessage(net.minecraft.network.chat.IChatBaseComponent message, java.util.UUID sender) {
+ entityplayer.sendMessage(message, sender);
+ }
+
+ @Override
+ public boolean shouldSendFailure() {
+ return true;
+ }
+ } : this;
+ // Paper end
// CraftBukkit - this
- return new CommandListenerWrapper(this, Vec3D.a((BaseBlockPosition) this.position), Vec2F.a, (WorldServer) this.world, 2, s, (IChatBaseComponent) object, this.world.getMinecraftServer(), entityplayer);
+ return new CommandListenerWrapper(commandSource, Vec3D.a((BaseBlockPosition) this.position), Vec2F.a, (WorldServer) this.world, 2, s, (IChatBaseComponent) object, this.world.getMinecraftServer(), entityplayer); // Paper
}
public EnumColor getColor() {
diff --git a/src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java b/src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java
index 8ddd246ad69a2e53749d38c369af701c161de54e..fd16506aeb92df86dd88eb3bb8091e5ab055760e 100644
--- a/src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java
+++ b/src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java
@@ -50,7 +50,7 @@ public class BukkitCommandWrapper implements com.mojang.brigadier.Command<Comman
@Override
public int run(CommandContext<CommandListenerWrapper> context) throws CommandSyntaxException {
- return server.dispatchCommand(context.getSource().getBukkitSender(), context.getInput()) ? 1 : 0;
+ return server.dispatchCommand(context.getSource().getBukkitSender(), context.getRange().get(context.getInput())) ? 1 : 0; // Paper - actually use the StringRange from context
}
@Override

View File

@ -1,40 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: mdcfe <1917406+mdcfe@users.noreply.github.com>
Date: Wed, 7 Jul 2021 09:44:45 -0700
Subject: [PATCH] Route sign run_command click events through normal chat logic
This patch changes sign command logic so that `run_command` click events are routed through the standard chat/command
logic used for inbound chat messages.
This fixes numerous issues related to sign click commands:
- Signs with a `run_command` value of "/<plugin command>" would fail and show the "Unknown command" warning. This
prevents usage of commands like `//wand` from WorldEdit in sign click events entirely and requires users to drop
the leading slash from other plugins' commands. This patch now executes the plugin commands as would be expected,
adding a leading slash if necessary.
- Signs with a `run_command` value that doesn't match an existing command could fail silently. This patch causes
these to *always* show "Unknown command" instead.
- Plugins listening to `PlayerCommandPreprocessEvent` would not be able to intercept any command executions from
sign click events. This patch allows plugins to intercept player commands when fired by a click event, in the same
manner as commands executed by the player typing or clicking on a chat message.
- Commands executed from signs would not be logged to the console. This patch fixes this.
This patch also prepends a leading slash if the `run_command` value lacks one, which matches vanilla behaviour (old
code would strip this slash away) while also ensuring `PlayerCommandPreprocessEvent#getMessage` remains consistent
with other command executions from chat (which always include the leading slash).
diff --git a/src/main/java/net/minecraft/world/level/block/entity/TileEntitySign.java b/src/main/java/net/minecraft/world/level/block/entity/TileEntitySign.java
index 7f78f388584899b13ff983f0dc37c679bfb1507e..2f51a5d9fdc4d73fd2228df7deb878d5880be1d8 100644
--- a/src/main/java/net/minecraft/world/level/block/entity/TileEntitySign.java
+++ b/src/main/java/net/minecraft/world/level/block/entity/TileEntitySign.java
@@ -155,7 +155,10 @@ public class TileEntitySign extends TileEntity implements ICommandListener { //
ChatClickable chatclickable = chatmodifier.getClickEvent();
if (chatclickable.a() == ChatClickable.EnumClickAction.RUN_COMMAND) {
- entityhuman.getMinecraftServer().getCommandDispatcher().a(this.a((EntityPlayer) entityhuman), chatclickable.b());
+ // Paper start - route through standard chat/command logic
+ final String command = chatclickable.b().startsWith("/") ? chatclickable.b() : "/" + chatclickable.b();
+ ((EntityPlayer) entityhuman).playerConnection.chat(command, false);
+ // Paper end
}
}
}