From 3a450ee1759b3df38d698daabdfafd0447d8223a Mon Sep 17 00:00:00 2001 From: Trumeet Date: Wed, 27 Jul 2022 11:04:45 -0700 Subject: feat(mod): enforce cmd response ordering and termination --- .../java/moe/ymc/acron/cmd/CmdResConsumer.java | 18 ++++++++- mod/src/main/java/moe/ymc/acron/cmd/CmdSrc.java | 45 +++++++++++++++++++++- 2 files changed, 60 insertions(+), 3 deletions(-) (limited to 'mod/src/main/java/moe/ymc/acron/cmd') diff --git a/mod/src/main/java/moe/ymc/acron/cmd/CmdResConsumer.java b/mod/src/main/java/moe/ymc/acron/cmd/CmdResConsumer.java index d22b77e..36f7605 100644 --- a/mod/src/main/java/moe/ymc/acron/cmd/CmdResConsumer.java +++ b/mod/src/main/java/moe/ymc/acron/cmd/CmdResConsumer.java @@ -16,6 +16,8 @@ public class CmdResConsumer implements ResultConsumer { private final @NotNull Channel channel; private final int id; + private boolean hasResult; + public CmdResConsumer(@NotNull Channel channel, int id) { this.channel = channel; @@ -28,6 +30,20 @@ public class CmdResConsumer implements ResultConsumer { id, success, result); - channel.writeAndFlush(Serializer.serialize(new EventCmdRes(id, success, result))); + // Ignore any failed results because CommandDispatcher#execute does not reliably send error + // results to the consumer. + // For example, fail results are either not sent at all (pre-processing errors), or + // sent before writing the result, which both violates the spec. + // We will send the failed results in a custom mixin. + if (success) { + hasResult = true; + channel.writeAndFlush(Serializer.serialize(new EventCmdRes(id, success, result))); + } + } + + public void sendResultIfNot() { + if (!hasResult) { + channel.writeAndFlush(Serializer.serialize(new EventCmdRes(id, false, -1))); + } } } diff --git a/mod/src/main/java/moe/ymc/acron/cmd/CmdSrc.java b/mod/src/main/java/moe/ymc/acron/cmd/CmdSrc.java index 983b4ed..9fde8a4 100644 --- a/mod/src/main/java/moe/ymc/acron/cmd/CmdSrc.java +++ b/mod/src/main/java/moe/ymc/acron/cmd/CmdSrc.java @@ -1,24 +1,34 @@ package moe.ymc.acron.cmd; +import com.mojang.brigadier.ResultConsumer; import io.netty.channel.Channel; import moe.ymc.acron.net.ClientConfiguration; import net.minecraft.command.argument.EntityAnchorArgumentType; +import net.minecraft.entity.Entity; import net.minecraft.server.MinecraftServer; +import net.minecraft.server.command.CommandOutput; import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.server.world.ServerWorld; import net.minecraft.text.LiteralText; +import net.minecraft.text.Text; +import net.minecraft.util.math.Vec2f; +import net.minecraft.util.math.Vec3d; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -class CmdSrc extends ServerCommandSource { +public class CmdSrc extends ServerCommandSource { private static final Logger LOGGER = LogManager.getLogger(); + private final CmdResConsumer resConsumer; + public CmdSrc(@NotNull Channel channel, int id, boolean display, @NotNull ClientConfiguration configuration, @NotNull MinecraftServer server) { - super(new CmdOut(channel, id, display), + this(new CmdOut(channel, id, display), configuration.pos(), configuration.rot(), configuration.world(), @@ -31,4 +41,35 @@ class CmdSrc extends ServerCommandSource { new CmdResConsumer(channel, id), EntityAnchorArgumentType.EntityAnchor.FEET); } + + public CmdSrc(CommandOutput output, + Vec3d pos, + Vec2f rot, + ServerWorld world, + int level, + String name, + Text displayName, + MinecraftServer server, + @Nullable Entity entity, + boolean silent, + CmdResConsumer consumer, + EntityAnchorArgumentType.EntityAnchor entityAnchor) { + super(output, + pos, + rot, + world, + level, + name, + displayName, + server, + entity, + silent, + consumer, + entityAnchor); + this.resConsumer = consumer; + } + + public void sendResultIfNot() { + resConsumer.sendResultIfNot(); + } } -- cgit v1.2.3