Add client commands (#319)

* Add client commands

* Bump version for release

Signed-off-by: shedaniel <daniel@shedaniel.me>

---------

Signed-off-by: shedaniel <daniel@shedaniel.me>
This commit is contained in:
shedaniel
2024-02-26 21:09:32 +08:00
committed by GitHub
parent 425b872c2a
commit f1817cc939
9 changed files with 229 additions and 8 deletions

View File

@@ -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<ClientCommandRegistrationEvent> 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<ClientCommandSourceStack> dispatcher);
static LiteralArgumentBuilder<ClientCommandSourceStack> literal(String name) {
return LiteralArgumentBuilder.literal(name);
}
static <T> RequiredArgumentBuilder<ClientCommandSourceStack, T> argument(String name, ArgumentType<T> 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();
}
}

View File

@@ -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<ClientCommandRegistrationEvent.ClientCommandSourceStack>)
(CommandDispatcher<?>) ClientCommandManager.DISPATCHER);
});
}
public static void registerCommon() {

View File

@@ -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();
}
}

View File

@@ -11,6 +11,7 @@
"client.MixinClientPacketListener",
"client.MixinDebugScreenOverlay",
"client.MixinEffectInstance",
"client.MixinFabricClientCommandSource",
"client.MixinGameRenderer",
"client.MixinIntegratedServer",
"client.MixinKeyboardHandler",

View File

@@ -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<ClientCommandRegistrationEvent.ClientCommandSourceStack>)
(CommandDispatcher<?>) event.getDispatcher());
}
@OnlyIn(Dist.CLIENT)
public static class ModBasedEventHandler {
@SubscribeEvent(priority = EventPriority.HIGH)

View File

@@ -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();
}
}

View File

@@ -5,6 +5,7 @@
"compatibilityLevel": "JAVA_16",
"minVersion": "0.8",
"client": [
"client.MixinCommandSourceStack",
"MixinClientLevel",
"MixinMinecraft"
],

View File

@@ -10,7 +10,7 @@ artifact_type=release
archives_base_name=architectury
archives_base_name_snapshot=architectury-snapshot
base_version=4.11
base_version=4.12
maven_group=dev.architectury
fabric_loader_version=0.14.19

View File

@@ -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;
})));
});
}
}
}