Jump to content

[Solved] Registering with CommandDispatcher to send a command already implemented in Minecraft


Frozen Storm

Recommended Posts

I'm trying to create a mod that lets me define command aliases in MC. I currently have the following code:

package com.extended_ui.commands;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.io.IOUtils;

import com.mojang.brigadier.Command;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.StringArgumentType;

import net.minecraft.client.Minecraft;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.network.chat.Component;

public class Alias {
	private static Map<String, String> aliases = new HashMap<String, String>();

	public static void registerCommands(CommandDispatcher<CommandSourceStack> dispatcher) {
		loadAliasesFromFiles();
		for (Map.Entry<String, String> entry : aliases.entrySet()) {
			registerAlias(dispatcher, entry.getKey(), entry.getValue());
		}

		dispatcher.register(Commands.literal("alias")
			.then(Commands.literal("-l")
				.executes((command) -> { return listAliases(); }))
			.then(Commands.literal("-r")
				.then(Commands.argument("alias", StringArgumentType.word())
					.executes((command) -> { return removeAlias(dispatcher, StringArgumentType.getString(command, "alias")); })))
			.then(Commands.argument("alias", StringArgumentType.word())
				.then(Commands.argument("command", StringArgumentType.greedyString())
					.executes((command) -> { return addAlias(dispatcher, StringArgumentType.getString(command, "alias"), StringArgumentType.getString(command, "command")); })))
		);
	}

	private static int listAliases() {
		Minecraft mc = Minecraft.getInstance();
		for (Map.Entry<String, String> entry : aliases.entrySet()) {
			mc.player.sendSystemMessage(Component.literal(entry.getKey() + ": " + entry.getValue()));
		}
		return Command.SINGLE_SUCCESS;
	}

	private static int addAlias(CommandDispatcher<CommandSourceStack> dispatcher, String alias, String command) {
		// Add alias to the map of aliases
		aliases.put(alias, command);

		// Save alias to file
		try {
			new File("config/aliases").mkdirs();
			Files.write(Paths.get("config/aliases/" + alias), Collections.singletonList(command), StandardCharsets.UTF_8);
		} catch (IOException e) {
			e.printStackTrace();
		}

		// Register new alias in the event dispatcher
		return registerAlias(dispatcher, alias, command);
	}

	private static int registerAlias(CommandDispatcher<CommandSourceStack> dispatcher, String alias, String command) {
		// dispatcher.register(Commands.literal(alias)
		// 	.then(Commands.argument("args", StringArgumentType.greedyString())
		// 		.redirect(command)));
		return Command.SINGLE_SUCCESS;
	}

	private static int removeAlias(CommandDispatcher<CommandSourceStack> dispatcher, String alias) {
		// Remove alias from the map of aliases
		aliases.remove(alias);

		// Remove alias file
		new File("config/aliases/" + alias).delete();

		// Remove alias from the event dispatcher
		// dispatcher.unregister(Commands.literal(alias));  // Not sure the dispatcher supports some way of unregistering commands. If it doesn't, then removing an alias properly will require a game restart
		return Command.SINGLE_SUCCESS;
	}

	private static void loadAliasesFromFiles() {
		File dir = new File("config/aliases");
		if (!(dir.isDirectory()))
			return;
		for (File file : dir.listFiles()) {
			String alias = file.getName();
			try (FileInputStream inputStream = new FileInputStream("foo.txt")) {
				String command = IOUtils.toString(inputStream, StandardCharsets.UTF_8).trim();
				aliases.put(alias, command);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

The methods for adding, removing, loading, and listing aliases all work properly (i.e., they add/remove/list the aliases in the aliases Map, and save/remove/load the aliases to/from files). However, right now registering an alias doesn't work, as I can't find a way to register an alias to simply execute the command as if it was sent in the game console. How would I implement the registerAlias method to properly register such a simple forward with the CommandDispatcher?

Edited by Frozen Storm
Link to comment
Share on other sites

I managed to get basic aliases to work using:

private static int registerAlias(CommandDispatcher<CommandSourceStack> dispatcher, String alias, String command) {
	dispatcher.register(Commands.literal(alias)
		.executes((commandContext) -> {
			dispatcher.execute(command, commandContext.getSource());
			return Command.SINGLE_SUCCESS;
		})
	);
}

 

I also managed to add the possibility for arguments in the alias, and my entire method looks like this now:

private static int registerAlias(CommandDispatcher<CommandSourceStack> dispatcher, String alias, String command) {
	Pattern argPattern = Pattern.compile("\\$\\{?(\\d+)}?");
	if (argPattern.matcher(command).find()) {
		dispatcher.register(Commands.literal(alias)
			.then(Commands.argument("args", StringArgumentType.greedyString())
				.executes((commandContext) -> {
					String commandString = command;
					String argString = StringArgumentType.getString(commandContext, "args");
					String[] args = ArgumentTokenizer.tokenize(argString).toArray(new String[0]);
					commandString = argPattern.matcher(commandString).replaceAll(
						match -> args[Integer.parseInt(match.group(1))-1]
					);
					dispatcher.execute(commandString, commandContext.getSource());
					return Command.SINGLE_SUCCESS;
				}))
		);
	} else {
		dispatcher.register(Commands.literal(alias)
			.executes((commandContext) -> {
				dispatcher.execute(command, commandContext.getSource());
				return Command.SINGLE_SUCCESS;
			})
		);
	}
	return Command.SINGLE_SUCCESS;
}

This way if a command contains any $1, $2, $10, ${1}, etc., it gets registered as an alias with arguments, otherwise it gets registered as a standalone. ArgumentTokenizer is the class I found here.

Edited by Frozen Storm
Different question for different thread
Link to comment
Share on other sites

  • Frozen Storm changed the title to [Solved] Registering with CommandDispatcher to send a command already implemented in Minecraft

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.