From 54531ce645b879de38c9bda97785a43d6faf7043 Mon Sep 17 00:00:00 2001 From: shedaniel Date: Fri, 12 Aug 2022 21:04:30 +0800 Subject: [PATCH] Add client commands --- .../ClientCommandRegistrationEvent.java | 71 +++++++++++++++++++ .../event/fabric/EventHandlerImpl.java | 12 ++-- .../MixinFabricClientCommandSource.java | 62 ++++++++++++++++ .../main/resources/architectury.mixins.json | 1 + .../event/forge/EventHandlerImplClient.java | 7 ++ .../forge/client/MixinCommandSourceStack.java | 67 +++++++++++++++++ .../main/resources/architectury.mixins.json | 1 + .../java/dev/architectury/test/TestMod.java | 14 +++- 8 files changed, 228 insertions(+), 7 deletions(-) create mode 100644 common/src/main/java/dev/architectury/event/events/client/ClientCommandRegistrationEvent.java create mode 100644 fabric/src/main/java/dev/architectury/mixin/fabric/client/MixinFabricClientCommandSource.java create mode 100644 forge/src/main/java/dev/architectury/mixin/forge/client/MixinCommandSourceStack.java diff --git a/common/src/main/java/dev/architectury/event/events/client/ClientCommandRegistrationEvent.java b/common/src/main/java/dev/architectury/event/events/client/ClientCommandRegistrationEvent.java new file mode 100644 index 00000000..3524587c --- /dev/null +++ b/common/src/main/java/dev/architectury/event/events/client/ClientCommandRegistrationEvent.java @@ -0,0 +1,71 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.event.events.client; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.builder.RequiredArgumentBuilder; +import dev.architectury.event.Event; +import dev.architectury.event.EventFactory; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.commands.SharedSuggestionProvider; +import net.minecraft.network.chat.Component; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.Vec2; +import net.minecraft.world.phys.Vec3; + +public interface ClientCommandRegistrationEvent { + /** + * @see ClientCommandRegistrationEvent#register(CommandDispatcher) + */ + Event EVENT = EventFactory.createLoop(); + + /** + * This event is invoked after the client initializes. + * Equivalent to Forge's {@code RegisterClientCommandsEvent} and Fabric's {@code ClientCommandManager}. + * + * @param dispatcher The command dispatcher to register commands to. + */ + void register(CommandDispatcher dispatcher); + + static LiteralArgumentBuilder literal(String name) { + return LiteralArgumentBuilder.literal(name); + } + + static RequiredArgumentBuilder argument(String name, ArgumentType type) { + return RequiredArgumentBuilder.argument(name, type); + } + + interface ClientCommandSourceStack extends SharedSuggestionProvider { + void arch$sendSuccess(Component message, boolean broadcastToAdmins); + + void arch$sendFailure(Component message); + + LocalPlayer arch$getPlayer(); + + Vec3 arch$getPosition(); + + Vec2 arch$getRotation(); + + ClientLevel arch$getLevel(); + } +} diff --git a/fabric/src/main/java/dev/architectury/event/fabric/EventHandlerImpl.java b/fabric/src/main/java/dev/architectury/event/fabric/EventHandlerImpl.java index ebf842b5..a01adaa4 100644 --- a/fabric/src/main/java/dev/architectury/event/fabric/EventHandlerImpl.java +++ b/fabric/src/main/java/dev/architectury/event/fabric/EventHandlerImpl.java @@ -19,13 +19,12 @@ package dev.architectury.event.fabric; -import dev.architectury.event.events.client.ClientGuiEvent; -import dev.architectury.event.events.client.ClientLifecycleEvent; -import dev.architectury.event.events.client.ClientTickEvent; -import dev.architectury.event.events.client.ClientTooltipEvent; +import com.mojang.brigadier.CommandDispatcher; +import dev.architectury.event.events.client.*; import dev.architectury.event.events.common.*; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.client.command.v1.ClientCommandManager; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; import net.fabricmc.fabric.api.client.item.v1.ItemTooltipCallback; @@ -54,6 +53,11 @@ public class EventHandlerImpl { ItemTooltipCallback.EVENT.register((itemStack, tooltipFlag, list) -> ClientTooltipEvent.ITEM.invoker().append(itemStack, list, tooltipFlag)); HudRenderCallback.EVENT.register((matrices, tickDelta) -> ClientGuiEvent.RENDER_HUD.invoker().renderHud(matrices, tickDelta)); + + ClientLifecycleEvent.CLIENT_STARTED.register(instance -> { + ClientCommandRegistrationEvent.EVENT.invoker().register((CommandDispatcher) + (CommandDispatcher) ClientCommandManager.DISPATCHER); + }); } public static void registerCommon() { diff --git a/fabric/src/main/java/dev/architectury/mixin/fabric/client/MixinFabricClientCommandSource.java b/fabric/src/main/java/dev/architectury/mixin/fabric/client/MixinFabricClientCommandSource.java new file mode 100644 index 00000000..0b58ce57 --- /dev/null +++ b/fabric/src/main/java/dev/architectury/mixin/fabric/client/MixinFabricClientCommandSource.java @@ -0,0 +1,62 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.mixin.fabric.client; + +import dev.architectury.event.events.client.ClientCommandRegistrationEvent; +import net.fabricmc.fabric.api.client.command.v1.FabricClientCommandSource; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.network.chat.Component; +import net.minecraft.world.phys.Vec2; +import net.minecraft.world.phys.Vec3; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(FabricClientCommandSource.class) +public interface MixinFabricClientCommandSource extends ClientCommandRegistrationEvent.ClientCommandSourceStack { + @Override + default void arch$sendSuccess(Component message, boolean broadcastToAdmins) { + ((FabricClientCommandSource) this).sendFeedback(message); + } + + @Override + default void arch$sendFailure(Component message) { + ((FabricClientCommandSource) this).sendError(message); + } + + @Override + default LocalPlayer arch$getPlayer() { + return ((FabricClientCommandSource) this).getPlayer(); + } + + @Override + default Vec3 arch$getPosition() { + return ((FabricClientCommandSource) this).getPosition(); + } + + @Override + default Vec2 arch$getRotation() { + return ((FabricClientCommandSource) this).getRotation(); + } + + @Override + default ClientLevel arch$getLevel() { + return ((FabricClientCommandSource) this).getWorld(); + } +} diff --git a/fabric/src/main/resources/architectury.mixins.json b/fabric/src/main/resources/architectury.mixins.json index 0674327c..3e6d6b94 100644 --- a/fabric/src/main/resources/architectury.mixins.json +++ b/fabric/src/main/resources/architectury.mixins.json @@ -11,6 +11,7 @@ "client.MixinClientPacketListener", "client.MixinDebugScreenOverlay", "client.MixinEffectInstance", + "client.MixinFabricClientCommandSource", "client.MixinGameRenderer", "client.MixinIntegratedServer", "client.MixinKeyboardHandler", diff --git a/forge/src/main/java/dev/architectury/event/forge/EventHandlerImplClient.java b/forge/src/main/java/dev/architectury/event/forge/EventHandlerImplClient.java index 84b55af5..6052edb5 100644 --- a/forge/src/main/java/dev/architectury/event/forge/EventHandlerImplClient.java +++ b/forge/src/main/java/dev/architectury/event/forge/EventHandlerImplClient.java @@ -20,6 +20,7 @@ package dev.architectury.event.forge; import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.brigadier.CommandDispatcher; import dev.architectury.event.CompoundEventResult; import dev.architectury.event.events.client.ClientChatEvent; import dev.architectury.event.events.client.*; @@ -327,6 +328,12 @@ public class EventHandlerImplClient { ClientRawInputEvent.KEY_PRESSED.invoker().keyPressed(Minecraft.getInstance(), event.getKey(), event.getScanCode(), event.getAction(), event.getModifiers()); } + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(RegisterClientCommandsEvent event) { + ClientCommandRegistrationEvent.EVENT.invoker().register((CommandDispatcher) + (CommandDispatcher) event.getDispatcher()); + } + @OnlyIn(Dist.CLIENT) public static class ModBasedEventHandler { @SubscribeEvent(priority = EventPriority.HIGH) diff --git a/forge/src/main/java/dev/architectury/mixin/forge/client/MixinCommandSourceStack.java b/forge/src/main/java/dev/architectury/mixin/forge/client/MixinCommandSourceStack.java new file mode 100644 index 00000000..65e6cc00 --- /dev/null +++ b/forge/src/main/java/dev/architectury/mixin/forge/client/MixinCommandSourceStack.java @@ -0,0 +1,67 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.mixin.forge.client; + +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import dev.architectury.event.events.client.ClientCommandRegistrationEvent; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.network.chat.Component; +import net.minecraft.world.phys.Vec2; +import net.minecraft.world.phys.Vec3; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(CommandSourceStack.class) +public abstract class MixinCommandSourceStack implements ClientCommandRegistrationEvent.ClientCommandSourceStack { + @Override + public void arch$sendSuccess(Component message, boolean broadcastToAdmins) { + ((CommandSourceStack) (Object) this).sendSuccess(message, broadcastToAdmins); + } + + @Override + public void arch$sendFailure(Component message) { + ((CommandSourceStack) (Object) this).sendFailure(message); + } + + @Override + public LocalPlayer arch$getPlayer() { + try { + return (LocalPlayer) ((CommandSourceStack) (Object) this).getEntityOrException(); + } catch (CommandSyntaxException e) { + throw new RuntimeException(e); + } + } + + @Override + public Vec3 arch$getPosition() { + return ((CommandSourceStack) (Object) this).getPosition(); + } + + @Override + public Vec2 arch$getRotation() { + return ((CommandSourceStack) (Object) this).getRotation(); + } + + @Override + public ClientLevel arch$getLevel() { + return (ClientLevel) ((CommandSourceStack) (Object) this).getUnsidedLevel(); + } +} diff --git a/forge/src/main/resources/architectury.mixins.json b/forge/src/main/resources/architectury.mixins.json index ceb99d84..44475b27 100644 --- a/forge/src/main/resources/architectury.mixins.json +++ b/forge/src/main/resources/architectury.mixins.json @@ -5,6 +5,7 @@ "compatibilityLevel": "JAVA_16", "minVersion": "0.8", "client": [ + "client.MixinCommandSourceStack", "MixinClientLevel", "MixinMinecraft" ], diff --git a/testmod-common/src/main/java/dev/architectury/test/TestMod.java b/testmod-common/src/main/java/dev/architectury/test/TestMod.java index 009bcf22..242c5906 100644 --- a/testmod-common/src/main/java/dev/architectury/test/TestMod.java +++ b/testmod-common/src/main/java/dev/architectury/test/TestMod.java @@ -19,12 +19,13 @@ package dev.architectury.test; +import com.mojang.brigadier.arguments.StringArgumentType; +import dev.architectury.event.events.client.ClientCommandRegistrationEvent; import dev.architectury.event.events.client.ClientLifecycleEvent; import dev.architectury.registry.client.level.entity.EntityRendererRegistry; import dev.architectury.test.debug.ConsoleMessageSink; import dev.architectury.test.debug.MessageSink; import dev.architectury.test.debug.client.ClientOverlayMessageSink; -import dev.architectury.test.entity.TestEntity; import dev.architectury.test.events.DebugEvents; import dev.architectury.test.gamerule.TestGameRules; import dev.architectury.test.item.TestBlockInteractions; @@ -41,8 +42,6 @@ import dev.architectury.utils.EnvExecutor; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.renderer.entity.CowRenderer; -import net.minecraft.client.renderer.entity.PigRenderer; -import net.minecraft.world.entity.animal.Cow; public class TestMod { public static final MessageSink SINK = EnvExecutor.getEnvSpecific(() -> ClientOverlayMessageSink::new, () -> ConsoleMessageSink::new); @@ -72,6 +71,15 @@ public class TestMod { TestModNet.initializeClient(); EntityRendererRegistry.register(TestRegistries.TEST_ENTITY, CowRenderer::new); EntityRendererRegistry.register(TestRegistries.TEST_ENTITY_2, CowRenderer::new); + ClientCommandRegistrationEvent.EVENT.register(dispatcher -> { + dispatcher.register(ClientCommandRegistrationEvent.literal("cool_client") + .then(ClientCommandRegistrationEvent.argument("string", StringArgumentType.string()) + .executes(context -> { + String string = StringArgumentType.getString(context, "string"); + SINK.accept("Cool client command for " + string); + return 0; + }))); + }); } } }