mirror of
https://github.com/GTNewHorizons/GTNHLib.git
synced 2026-10-26 14:24:10 +00:00
Add Brigadier (#149)
Co-authored-by: Jason Mitchell <mitchej@gmail.com> Co-authored-by: Martin Robertz <dream-master@gmx.net> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This commit is contained in:
8 files changed
+296
-2
No files matched your search
@@ -6,8 +6,10 @@ configurations {
|
||||
dependencies {
|
||||
shadowImplementation("it.unimi.dsi:fastutil:8.5.15") // Apache 2.0
|
||||
shadowImplementation("org.joml:joml:1.10.8") { transitive = false } // MIT
|
||||
shadowImplementation("com.mojang:brigadier:1.0.18") // MIT
|
||||
shadowSources("it.unimi.dsi:fastutil:8.5.15:sources")
|
||||
shadowSources("org.joml:joml:1.10.8:sources") { transitive = false }
|
||||
shadowSources("com.mojang:brigadier:1.0.18")
|
||||
|
||||
testImplementation(platform("org.junit:junit-bom:5.11.4"))
|
||||
testImplementation("org.junit.jupiter:junit-jupiter")
|
||||
|
||||
@@ -8,4 +8,11 @@ repositories {
|
||||
includeGroup 'com.falsepattern'
|
||||
}
|
||||
}
|
||||
maven {
|
||||
name = "Mojang Maven"
|
||||
url = "https://libraries.minecraft.net"
|
||||
content {
|
||||
includeGroup 'com.mojang'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import net.minecraft.util.IChatComponent;
|
||||
import net.minecraftforge.common.util.FakePlayer;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.block.BlockTest;
|
||||
import com.gtnewhorizon.gtnhlib.brigadier.BrigadierApi;
|
||||
import com.gtnewhorizon.gtnhlib.config.ConfigurationManager;
|
||||
import com.gtnewhorizon.gtnhlib.eventbus.AutoEventBus;
|
||||
import com.gtnewhorizon.gtnhlib.eventbus.EventBusSubscriber;
|
||||
@@ -58,13 +59,17 @@ public class CommonProxy {
|
||||
|
||||
public void postInit(FMLPostInitializationEvent event) {}
|
||||
|
||||
public void serverAboutToStart(FMLServerAboutToStartEvent event) {}
|
||||
public void serverAboutToStart(FMLServerAboutToStartEvent event) {
|
||||
BrigadierApi.init();
|
||||
}
|
||||
|
||||
public void serverStarting(FMLServerStartingEvent event) {}
|
||||
|
||||
public void serverStarted(FMLServerStartedEvent event) {}
|
||||
|
||||
public void serverStopping(FMLServerStoppingEvent event) {}
|
||||
public void serverStopping(FMLServerStoppingEvent event) {
|
||||
BrigadierApi.clear();
|
||||
}
|
||||
|
||||
public void serverStopped(FMLServerStoppedEvent event) {}
|
||||
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
package com.gtnewhorizon.gtnhlib.brigadier;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import net.minecraft.command.CommandException;
|
||||
import net.minecraft.command.ICommand;
|
||||
import net.minecraft.command.ICommandSender;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.util.ChatComponentText;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.ParseResults;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.suggestion.Suggestion;
|
||||
import com.mojang.brigadier.tree.CommandNode;
|
||||
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
|
||||
@Log4j2
|
||||
public class BrigadierApi {
|
||||
|
||||
@Nullable
|
||||
private static CommandDispatcher<ICommandSender> dispatcher = new CommandDispatcher<>();
|
||||
|
||||
@NotNull
|
||||
public static CommandDispatcher<ICommandSender> getCommandDispatcher() {
|
||||
return Objects.requireNonNull(dispatcher);
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static void init() {
|
||||
dispatcher = new CommandDispatcher<>();
|
||||
registerTestCommand();
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static void clear() {
|
||||
dispatcher = null;
|
||||
}
|
||||
|
||||
private static void registerTestCommand() {
|
||||
getCommandDispatcher().register(LiteralArgumentBuilder.<ICommandSender>literal("brigadier").executes(ctx -> {
|
||||
ctx.getSource().addChatMessage(new ChatComponentText("Brigadier from GTNHLib!"));
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}).then(
|
||||
RequiredArgumentBuilder
|
||||
.<ICommandSender, String>argument("stringValue", StringArgumentType.greedyString())
|
||||
.requires(src -> src.canCommandSenderUseCommand(4, "")).suggests((ctx, builder) -> {
|
||||
for (EntityPlayer playerEntity : ctx.getSource().getEntityWorld().playerEntities) {
|
||||
builder.suggest(playerEntity.getCommandSenderName());
|
||||
}
|
||||
for (int i = 0; i < 9; i++) {
|
||||
builder.suggest(i);
|
||||
}
|
||||
return builder.buildFuture();
|
||||
}).executes(ctx -> {
|
||||
String value = StringArgumentType.getString(ctx, "stringValue");
|
||||
ctx.getSource().addChatMessage(new ChatComponentText("Brigadier: " + value));
|
||||
return Command.SINGLE_SUCCESS;
|
||||
})));
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the command by brigadier.
|
||||
*
|
||||
* @param sender the executor
|
||||
* @param command the command to execute
|
||||
* @return the result
|
||||
*/
|
||||
public static int executeCommand(ICommandSender sender, String command) {
|
||||
try {
|
||||
return getCommandDispatcher().execute(command, sender);
|
||||
} catch (CommandSyntaxException e) {
|
||||
// TODO: make the exception message better
|
||||
throw new CommandException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the suggestions for the given sender with the given typed command.
|
||||
*
|
||||
* @param sender the given sender
|
||||
* @param command the given typed command
|
||||
* @return the suggestions in text
|
||||
*/
|
||||
public static List<String> getPossibleCommands(ICommandSender sender, String command) {
|
||||
ParseResults<ICommandSender> parse = getCommandDispatcher().parse(command, sender);
|
||||
try {
|
||||
return getCommandDispatcher().getCompletionSuggestions(parse).get().getList().stream()
|
||||
.map(Suggestion::getText).collect(Collectors.toList());
|
||||
} catch (ExecutionException | InterruptedException e) {
|
||||
log.warn("Exception occurred while completing command '{}'", command, e);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the wrapped commands that the given sender has the permission to execute.
|
||||
*
|
||||
* @param sender the given sender
|
||||
* @return the wrapped commands
|
||||
*/
|
||||
public static Collection<ICommand> getPossibleCommands(ICommandSender sender) {
|
||||
return getCommandDispatcher().getRoot().getChildren().stream().filter(c -> c.canUse(sender))
|
||||
.map(BrigadierApi::toICommandForHelpCommand).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap the given command node to {@link ICommand} with only used methods implemented.
|
||||
*
|
||||
* @param node the given command node
|
||||
* @return the wrapper
|
||||
*/
|
||||
private static ICommand toICommandForHelpCommand(CommandNode<?> node) {
|
||||
return new ICommand() {
|
||||
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return node.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommandUsage(ICommandSender sender) {
|
||||
return node.getUsageText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getCommandAliases() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processCommand(ICommandSender sender, String[] args) {}
|
||||
|
||||
@Override
|
||||
public boolean canCommandSenderUseCommand(ICommandSender sender) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> addTabCompletionOptions(ICommandSender sender, String[] args) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUsernameIndex(String[] args, int index) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull Object o) {
|
||||
if (o instanceof ICommand otherCommand) {
|
||||
return getCommandName().compareTo(otherCommand.getCommandName());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DummyICommand{" + node.toString() + "}";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return node.hashCode();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -24,6 +24,7 @@ public enum Mixins implements IMixins {
|
||||
DEBUG_TEXTURES(new MixinBuilder("Dump textures sizes")
|
||||
.addClientMixins("debug.MixinDynamicTexture", "debug.MixinTextureAtlasSprite").setPhase(Phase.EARLY)
|
||||
.setApplyIf(() -> Boolean.parseBoolean(System.getProperty("gtnhlib.debugtextures", "false")))),
|
||||
BRIGADIER(Side.COMMON, "MixinCommandHandler", "MixinCommandHelp"),
|
||||
FONT_RENDERER(new MixinBuilder("Font rendering replacements").addClientMixins("MixinFontRenderer")
|
||||
.setPhase(Phase.EARLY).setApplyIf(() -> EarlyConfig.enableFontRendererMixin)),
|
||||
MODEL_TEXTURE_LOADING(new MixinBuilder("Automatically load model textures").addClientMixins(
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.gtnewhorizon.gtnhlib.mixins.early;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.command.CommandHandler;
|
||||
import net.minecraft.command.ICommand;
|
||||
import net.minecraft.command.ICommandSender;
|
||||
import net.minecraft.command.ServerCommandManager;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.brigadier.BrigadierApi;
|
||||
|
||||
@Mixin(CommandHandler.class)
|
||||
public class MixinCommandHandler {
|
||||
|
||||
@Unique
|
||||
private boolean gtnhlib$isServerCommandManager() {
|
||||
// this is used to make sure that we only inject into the server command handler
|
||||
return (Object) this instanceof ServerCommandManager;
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "executeCommand",
|
||||
at = @At(value = "NEW", target = "()Lnet/minecraft/command/CommandNotFoundException;"),
|
||||
cancellable = true)
|
||||
private void gtnhlib$fallbackToBrigadier(ICommandSender sender, String command,
|
||||
CallbackInfoReturnable<Integer> cir) {
|
||||
if (gtnhlib$isServerCommandManager()) {
|
||||
int value = BrigadierApi.executeCommand(sender, command);
|
||||
cir.setReturnValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "getPossibleCommands(Lnet/minecraft/command/ICommandSender;Ljava/lang/String;)Ljava/util/List;",
|
||||
at = @At("RETURN"),
|
||||
cancellable = true)
|
||||
private void gtnhlib$gatherBrigadierSuggestion(ICommandSender sender, String command,
|
||||
CallbackInfoReturnable<List<String>> cir) {
|
||||
if (gtnhlib$isServerCommandManager()) {
|
||||
List<String> returnValue = cir.getReturnValue();
|
||||
List<String> possibleCommands = BrigadierApi.getPossibleCommands(sender, command);
|
||||
if (returnValue == null) {
|
||||
cir.setReturnValue(possibleCommands);
|
||||
} else {
|
||||
returnValue.addAll(possibleCommands);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "getPossibleCommands(Lnet/minecraft/command/ICommandSender;)Ljava/util/List;", at = @At("RETURN"))
|
||||
private void gtnhlib$gatherBrigadierCommands(ICommandSender sender, CallbackInfoReturnable<List<ICommand>> cir) {
|
||||
if (gtnhlib$isServerCommandManager()) {
|
||||
Collection<ICommand> possibleCommands = BrigadierApi.getPossibleCommands(sender);
|
||||
cir.getReturnValue().addAll(possibleCommands);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.gtnewhorizon.gtnhlib.mixins.early;
|
||||
|
||||
import net.minecraft.command.CommandHelp;
|
||||
import net.minecraft.command.ICommandSender;
|
||||
import net.minecraft.command.WrongUsageException;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.brigadier.BrigadierApi;
|
||||
import com.mojang.brigadier.tree.CommandNode;
|
||||
|
||||
@Mixin(CommandHelp.class)
|
||||
public class MixinCommandHelp {
|
||||
|
||||
@Inject(
|
||||
method = "processCommand",
|
||||
at = @At(value = "NEW", target = "()Lnet/minecraft/command/CommandNotFoundException;"),
|
||||
cancellable = true)
|
||||
private void gtnhlib$fallbackToBrigadier(ICommandSender sender, String[] args, CallbackInfo ci) {
|
||||
CommandNode<ICommandSender> node = BrigadierApi.getCommandDispatcher().getRoot().getChild(args[0]);
|
||||
if (node != null) {
|
||||
throw new WrongUsageException(node.getUsageText());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,3 +18,5 @@ public net.minecraft.client.renderer.Tessellator field_78415_z # isDrawing
|
||||
public net.minecraft.client.renderer.Tessellator field_78388_E # bufferSize
|
||||
public net.minecraft.client.renderer.Tessellator field_78410_t # isColorDisabled
|
||||
public net.minecraft.client.renderer.Tessellator func_78379_d()V # void reset()
|
||||
|
||||
public net.minecraft.command.CommandHandler field_71562_a # commandMap
|
||||
Reference in new issue
Block a user