Skip to content
This repository has been archived by the owner on Mar 17, 2023. It is now read-only.

Custom ArgumentTypes

BotulToxin edited this page May 6, 2021 · 3 revisions

Before creating your own ArgumentType<T>, make sure that neither Brigadier nor Grenadier provides what you're looking for.

You can check all inbuilt types here and all Brigadier types here

Creating a custom type

I'm going to use WorldArgument as an example for this.

Firstly, create a class which implements ArgumentType<World> and implements the parse(StringReader reader) method like so:

public class WorldArgumentType implements ArgumentType<World> {
	public World parse(StringReader reader){
	}
}

Now we first create an exception provider for unknown world names. It should look like this: public static final DynamicCommandExceptionType UNKNOWN_WORLD = new DynamicCommandExceptionType(worldName -> new LiteralMessage("Unknown world: " + worldName));

And then we'll make a simple parse method where we read a world name and get the world from that, like so:

public class WorldArgumentType implements ArgumentType<World> {
	public static final DynamicCommandExceptionType UNKNOWN_WORLD = new DynamicCommandExceptionType(inputMessage -> new LiteralMessage("Unknown world: " + inputMessage));

	public World parse(StringReader reader) throws CommandSyntaxException {
		int cursor = reader.getCursor(); //Will be used for placing the cursor in the correct position
		String name = reader.readUnqotedString(); //Reads the world name

		World result = Bukkit.getWorld(name);
		if(result == null){ //If world wasn't found
			reader.setCursor(cursor); //Set cursor back into correct position
			throw UNKNOWN_WORLD.createWithContext(reader, name);
		}

		return result;
	}
}

That's the base now done, the next part is optional and can be skipped over

Suggestions

The ArgumentType<T> interface also includes the listSuggestions(CommandContext, SuggestionsBuilder); method for listing tab completions.

We can implement this method to list world name suggestions like so:

public CompletableFuture<Suggestions> listSuggestions(CommandContext context, SuggestionsBuilder){
	String token = builder.getRemaining().toLowerCase(); //What the user has already typed in
	
	for(World w: Bukkit.getWorlds()){
		//If world name matches what's already been typed in, suggest it
		if(w.getName().toLowerCase().startsWith(token)) builder.suggest(w.getName());
	}
	
	return builder.buildFuture();
}

If what you want to suggest is already a list or collection of strings, you can use CommandSource.suggestMatching(SuggestionsBuilder, Iterable<String>) to quickly suggest items.

Argument Instance

To use the argument in commands we'll need an instance of it to use.

We do this by creating a static final instance variable like so: private static final WorldArgumentType WORLD = new WorldArgumentType();

Personally, I like to create a static method that returns that value instead of using the constant directly, in this case, it would look like this:

public static WorldArgumentType world(){
	return WORLD;
}

Registering the ArgumentType

Finally, we have to register the argument type. We can do this by simple calling RoyalArguments.register(WorldArgumentType.class, VanillaArgumentType.WORD); in onEnable or onLoad.

Using the ArgumentType

Now we can easily use the argument type like so:

protected void createCommand(BrigadierCommand command){
	command.then(argument("world", WorldArgumentType.world())
		.executes(context -> {
			CommandSource source = context.getSource();
			World world = context.getArgument("world", World.class);

			source.sendMessage(world.getName());
			return 0;
		})
	)
}