diff --git a/src/main/java/org/cardboardpowered/mixin/commands/CommandsMixin.java b/src/main/java/org/cardboardpowered/mixin/commands/CommandsMixin.java index 60b26986..3d689e8d 100644 --- a/src/main/java/org/cardboardpowered/mixin/commands/CommandsMixin.java +++ b/src/main/java/org/cardboardpowered/mixin/commands/CommandsMixin.java @@ -20,6 +20,7 @@ import net.minecraft.commands.CommandBuildContext; import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.Commands; +import net.minecraft.network.protocol.game.ClientboundCommandsPacket; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.permissions.PermissionProviderCheck; @@ -30,6 +31,7 @@ import org.cardboardpowered.bridge.commands.PermissionProviderCheckBridge; import org.cardboardpowered.bridge.server.level.ServerPlayerBridge; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; @@ -46,6 +48,10 @@ public class CommandsMixin { @Shadow public com.mojang.brigadier.CommandDispatcher dispatcher; + @Shadow + @Final + private static ClientboundCommandsPacket.NodeInspector COMMAND_NODE_INSPECTOR; + @Shadow private static void fillUsableCommands( CommandNode root, @@ -81,58 +87,58 @@ private static void fillUsableCommands( } } + /** + * Build the command tree for this specific player, expose its top-level + * labels through Bukkit's PlayerCommandSendEvent, then send a root that + * contains only labels retained by plugins. + * + * Cardboard previously fired PlayerCommandSendEvent but ignored changes to + * event.getCommands(), which made command-hiding plugins unable to remove + * commands from client-side slash/TAB suggestions. + */ @SuppressWarnings({"rawtypes", "unchecked"}) - @Inject(at = @At("HEAD"), method = "sendCommands") - public void bukkitize( + @Inject(at = @At("HEAD"), method = "sendCommands", cancellable = true) + private void cardboard$filterAndSendCommands( ServerPlayer entityplayer, CallbackInfo ci ) { Map, CommandNode> map = Maps.newIdentityHashMap(); - RootCommandNode vanillaRoot = new RootCommandNode(); - - RootCommandNode vanilla = - entityplayer.level() - .getServer() - .getCommands() - .getDispatcher() - .getRoot(); - - map.put(vanilla, vanillaRoot); - - fillUsableCommands( - vanilla, - vanillaRoot, - entityplayer.createCommandSourceStack(), - (Map) map - ); - - RootCommandNode rootcommandnode = + RootCommandNode rootCommandNode = new RootCommandNode<>(); - map.put(this.dispatcher.getRoot(), rootcommandnode); + map.put(this.dispatcher.getRoot(), rootCommandNode); fillUsableCommands( this.dispatcher.getRoot(), - rootcommandnode, + rootCommandNode, entityplayer.createCommandSourceStack(), (Map) map ); - Collection bukkit = new LinkedHashSet<>(); + Collection originalCommands = new LinkedHashSet<>(); + for (CommandNode node : rootCommandNode.getChildren()) { + originalCommands.add(node.getName()); + } + + PlayerCommandSendEvent event = new PlayerCommandSendEvent( + (Player) ((ServerPlayerBridge) entityplayer).getBukkitEntity(), + new LinkedHashSet<>(originalCommands) + ); + CraftEventFactory.callEvent(event); - for (CommandNode node : rootcommandnode.getChildren()) { - bukkit.add(node.getName()); + RootCommandNode filteredRoot = new RootCommandNode<>(); + for (CommandNode node : rootCommandNode.getChildren()) { + if (event.getCommands().contains(node.getName())) { + filteredRoot.addChild(node); + } } - PlayerCommandSendEvent event = - new PlayerCommandSendEvent( - (Player) ((ServerPlayerBridge) entityplayer) - .getBukkitEntity(), - new LinkedHashSet<>(bukkit) - ); + entityplayer.connection.send( + new ClientboundCommandsPacket(filteredRoot, COMMAND_NODE_INSPECTOR) + ); - CraftEventFactory.callEvent(event); + ci.cancel(); } -} \ No newline at end of file +}