diff --git a/src/main/java/net/earthcomputer/clientcommands/ClientCommands.java b/src/main/java/net/earthcomputer/clientcommands/ClientCommands.java index 91996737e..e6e5fb338 100644 --- a/src/main/java/net/earthcomputer/clientcommands/ClientCommands.java +++ b/src/main/java/net/earthcomputer/clientcommands/ClientCommands.java @@ -159,6 +159,7 @@ public static void registerCommands(CommandDispatcher PosCommand.register(dispatcher); CrackRNGCommand.register(dispatcher); WeatherCommand.register(dispatcher); + PluginsCommand.register(dispatcher); Calendar calendar = Calendar.getInstance(); boolean registerChatCommand = calendar.get(Calendar.MONTH) == Calendar.APRIL && calendar.get(Calendar.DAY_OF_MONTH) == 1; diff --git a/src/main/java/net/earthcomputer/clientcommands/command/PluginsCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/PluginsCommand.java new file mode 100644 index 000000000..9997bfa66 --- /dev/null +++ b/src/main/java/net/earthcomputer/clientcommands/command/PluginsCommand.java @@ -0,0 +1,79 @@ +package net.earthcomputer.clientcommands.command; + +import com.mojang.brigadier.Command; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.suggestion.Suggestion; +import com.mojang.brigadier.tree.CommandNode; +import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; +import net.minecraft.network.packet.c2s.play.RequestCommandCompletionsC2SPacket; +import net.minecraft.network.packet.s2c.play.CommandSuggestionsS2CPacket; +import net.minecraft.text.Text; + +import java.util.stream.Collectors; + +import static com.mojang.brigadier.arguments.StringArgumentType.*; +import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*; + +public class PluginsCommand { + + private static boolean awaitingSuggestionsPacket = false; + + private static SuggestionsCallback callback; + + public static void register(CommandDispatcher dispatcher) { + dispatcher.register(literal("cplugins") + .executes(ctx -> getPlugins(ctx.getSource())) + .then(literal("partial") + .then(argument("partial", greedyString()) + .executes(ctx -> getPlugins(ctx.getSource(), getString(ctx, "partial"))))) + .then(literal("dispatcher") + .executes(ctx -> getPluginsFromDispatcher(ctx.getSource())))); + } + + private static int getPlugins(FabricClientCommandSource source) { + return getPlugins(source, ""); + } + + private static int getPlugins(FabricClientCommandSource source, String partial) { + awaitingSuggestionsPacket = true; + source.getClient().getNetworkHandler().sendPacket(new RequestCommandCompletionsC2SPacket(1, partial)); + callback = packet -> { + String plugins = packet.getSuggestions().getList().stream() + .map(Suggestion::getText) + .filter(text -> text.contains(":")) + .map(text -> text.substring(0, text.indexOf(":"))) + .distinct() + .collect(Collectors.joining(", ")); + + source.sendFeedback(Text.translatable("commands.cplugins.found")); + source.sendFeedback(Text.literal(plugins)); + }; + return Command.SINGLE_SUCCESS; + } + + private static int getPluginsFromDispatcher(FabricClientCommandSource source) { + String plugins = source.getClient().getNetworkHandler().getCommandDispatcher().getRoot().getChildren().stream() + .map(CommandNode::getName) + .filter(name -> name.contains(":")) + .map(name -> name.substring(0, name.indexOf(":"))) + .distinct() + .collect(Collectors.joining(", ")); + + source.sendFeedback(Text.translatable("commands.cplugins.found")); + source.sendFeedback(Text.literal(plugins)); + return Command.SINGLE_SUCCESS; + } + + public static void onCommandSuggestions(CommandSuggestionsS2CPacket packet) { + if (!awaitingSuggestionsPacket) { + return; + } + awaitingSuggestionsPacket = false; + callback.apply(packet); + } +} + +@FunctionalInterface +interface SuggestionsCallback { + void apply(CommandSuggestionsS2CPacket packet); +} diff --git a/src/main/java/net/earthcomputer/clientcommands/mixin/MixinClientPlayNetworkHandler.java b/src/main/java/net/earthcomputer/clientcommands/mixin/MixinClientPlayNetworkHandler.java index 3f05a1e69..45f9c1862 100644 --- a/src/main/java/net/earthcomputer/clientcommands/mixin/MixinClientPlayNetworkHandler.java +++ b/src/main/java/net/earthcomputer/clientcommands/mixin/MixinClientPlayNetworkHandler.java @@ -4,6 +4,7 @@ import net.cortex.clientAddon.cracker.SeedCracker; import net.earthcomputer.clientcommands.ClientcommandsDataQueryHandler; import net.earthcomputer.clientcommands.Configs; +import net.earthcomputer.clientcommands.command.PluginsCommand; import net.earthcomputer.clientcommands.features.FishingCracker; import net.earthcomputer.clientcommands.features.PlayerRandCracker; import net.minecraft.client.MinecraftClient; @@ -13,6 +14,7 @@ import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.entity.EntityType; import net.minecraft.network.ClientConnection; +import net.minecraft.network.packet.s2c.play.CommandSuggestionsS2CPacket; import net.minecraft.network.packet.s2c.play.EntitySpawnS2CPacket; import net.minecraft.network.packet.s2c.play.ExperienceOrbSpawnS2CPacket; import net.minecraft.network.packet.s2c.play.NbtQueryResponseS2CPacket; @@ -98,4 +100,9 @@ private void onOnNbtQueryResponse(NbtQueryResponseS2CPacket packet, CallbackInfo public ClientcommandsDataQueryHandler clientcommands_getCCDataQueryHandler() { return ccDataQueryHandler; } + + @Inject(method = "onCommandSuggestions", at = @At("TAIL")) + private void onCommandSuggestions(CommandSuggestionsS2CPacket packet, CallbackInfo ci) { + PluginsCommand.onCommandSuggestions(packet); + } } diff --git a/src/main/resources/assets/clientcommands/lang/en_us.json b/src/main/resources/assets/clientcommands/lang/en_us.json index b0a39949f..2366a582c 100644 --- a/src/main/resources/assets/clientcommands/lang/en_us.json +++ b/src/main/resources/assets/clientcommands/lang/en_us.json @@ -147,6 +147,8 @@ "commands.cplaysound.success": "Played sound %s to self", + "commands.cplugins.found": "Found the following plugins:", + "commands.cping.success": "Your average ping is %dms", "commands.cping.success.other": "The average ping of %s is %dms", "commands.cping.singleplayer": "You are in a singleplayer world (╯°□°)╯︵ ┻━┻",