Skip to content

[Suggestion] Optional Arguments #110

Description

@Speiger

Ok this is a bit rough to start.

When commands in Minecraft are created, you have either 2 ways of creating commands that are executed.
Either implement every single execution itself. (If you have 5 optional parameters that's getting annoying really quickly)
Or you implement a CommandContext Wrapper that basically does this:

	public boolean hasValue(String id, Class<?> type)
	{
		try
		{
			return source.getArgument(id, type) != null;
		}
		catch(Exception e)
		{
			return false;
		}
	}
	
	public <T> T getOrDefault(String id, Class<T> type, T defaultValue)
	{
		try
		{
			return source.getArgument(id, type);
		}
		catch(Exception e)
		{
		}
		return defaultValue;
	}

It would be nice if you could check if arguments have been defined or allow to getOrDefault.
This is mainly there to reduce code. Implementing everything 5 times. Even if Lambdas are being used that call a helper function that basically does the same.
Instead this could look a lot cleaner.

Here is a example with my CommandBuilder & Command Wrapper

	public static CommandBuilder createGenStart()
	{
		CommandBuilder builder = new CommandBuilder("gen");
		//Normal Gen
		Command<CommandSource> radius = GenCommand::executeRadius;
		builder.addLiteral("radius");
		builder.addArgument("Task Name", StringArgumentType.word());
		builder.addArgument("Shape", StringArgumentType.word(), GenCommand::listShape);
		builder.addArgument("Center", ColumnPosArgument.columnPos(), CenterArgument::listSimpleSuggestion);
		builder.addArgument("Radius", IntegerArgumentType.integer(1, 25000), radius);
		builder.addArgument("Dimension", DimensionArgument.getDimension(), radius); //Optional
		builder.addArgument("Generation Type", StringArgumentType.word(), GenCommand::listSuggestions, radius).popTop(); //PopTop basically goes down the logic tree allowing to make a new branch. A single/multi pop exists too.
		
		Command<CommandSource> expansion = GenCommand::executeExpansion;
		builder.addLiteral("expansion");
		builder.addArgument("Task Name", StringArgumentType.word());
		builder.addArgument("Shape", StringArgumentType.word(), GenCommand::listShape);
		builder.addArgument("Center", ColumnPosArgument.columnPos(), CenterArgument::listSimpleSuggestion);
		builder.addArgument("Min Radius", IntegerArgumentType.integer(1));
		builder.addArgument("Max Radius", IntegerArgumentType.integer(1), expansion);
		builder.addArgument("Dimension", DimensionArgument.getDimension(), expansion);
		builder.addArgument("Generation Type", StringArgumentType.word(), GenCommand::listSuggestions, expansion).popTop();
		return builder;
	}
	
	private static int executeRadius(CommandContext<CommandSource> source)
	{
		CommandWrapper wrapper = new CommandWrapper(source);
		String name = wrapper.get("Task Name", String.class);
		GenShape shape = wrapper.hasValue("Shape", String.class) ? GenShape.valueOf(wrapper.get("Shape", String.class)) : wrapper.get("Shape", GenShape.class);
		BlockPos center = CenterArgument.getVanillaBlockPos(wrapper.get("Center", ILocationArgument.class), source.getSource());
		int radius = wrapper.get("Radius", Integer.class);
		ResourceLocation dimension = wrapper.getOrDefault("Dimension", ResourceLocation.class, wrapper.getSource().getWorld().getDimensionKey().getLocation());
		return 0;
	}

The Reason for hasValue is used for the GenShape is a example. What if the software that the CommandContext uses only exists on 1 side. It can detect if a single side exists, or if dual sided implementation is present.
Or in modding terms: ServerOnly Mods.

This is how I would implement it.
If you want I can make it a offical PR, or you can just grab the implementation.
Reference Code: https://github.com/Mojang/brigadier/blob/master/src/main/java/com/mojang/brigadier/context/CommandContext.java#L81

    public <V> boolean hasArgument(final String name, final Class<V> clazz) {
        final ParsedArgument<S, ?> argument = arguments.get(name);

        if (argument == null) {
            return false;
        }
        final Object result = argument.getResult();
        if (PRIMITIVE_TO_WRAPPER.getOrDefault(clazz, clazz).isAssignableFrom(result.getClass())) {
            return true;
        } else {
            return false;
        }
    }
	
    public <V> V getArgument(final String name, final Class<V> clazz, V defaultValue) {
        final ParsedArgument<S, ?> argument = arguments.get(name);

        if (argument == null) {
            return defaultValue;
        }

        final Object result = argument.getResult();
        if (PRIMITIVE_TO_WRAPPER.getOrDefault(clazz, clazz).isAssignableFrom(result.getClass())) {
            return (V) result;
        } else {
            return defaultValue;
        }
    }

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions