Skip to content

Create Commands

Eric Lam edited this page Jun 13, 2022 · 5 revisions

Create A Normal Command

Create A single Command is simple:

create a command:

@Commander(
        name = "test",
        description = "test command",
        alias = {"tes", "te"}
)
public class TestCommand implements CommandNode {

    @Override
    public void execute(CommandSender commandSender) {
       commandSender.sendMessage("test!");
    }

}

and register:

public class TesterRegistry implements ComponentsRegistry {


    @Override
    public void registerCommand(CommandRegistry<CommandSender> commandRegistry) {
        commandRegistry.command(TestCommand.class);
    }

    @Override
    public void registerListeners(ListenerRegistry<Listener> listenerRegistry) {
    }


}

Create A Command With SubCommands

root command

@Commander(
        name = "test",
        description = "test command",
        alias = {"tes", "te"}
)
public class TestCommand implements CommandNode {

    @Override
    public void execute(CommandSender commandSender) {
    }

}

subcommand 1

@Commander(
        name = "one",
        description = "test one command"
)
public class TestOneCommand implements CommandNode {

    // here is the command arguments, which we will talk later
    @CommandArg(order = 0, labels = {"string"})
    private String value;

    @Override
    public void execute(CommandSender commandSender) {
        commandSender.sendMessage("this is one command with value "+value);
    }
}

subcommand 2

@Commander(
        name = "two",
        description = "two command"
)
public class TestTwoCommand implements CommandNode {

    @CommandArg(order = 0)
    private int number;

    @Override
    public void execute(CommandSender commandSender) {
        commandSender.sendMessage("this is two command with number "+number);
    }
}

and define their relationship:

public class TesterRegistry implements ComponentsRegistry {


    @Override
    public void registerCommand(CommandRegistry<CommandSender> commandRegistry) {
        commandRegistry.command(TestCommand.class, c -> {
            
            c.command(TestOneCommand.class);
            
            c.command(TestTwoCommand.class);
            
        });
    }

    @Override
    public void registerListeners(ListenerRegistry<Listener> listenerRegistry) {
    }


}

After that, the command will be registered as below:

/test one <string>
/test two <number>

The Commander Annotation Reference:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Commander {

    String name();

    String description();

    boolean playerOnly() default false; // still need to cast yourself

    String permission() default "";

    String[] alias() default {};

}

Guess what? you can create multiple of them as you can!

public class TesterRegistry implements ComponentsRegistry {

    // 本框架允許你無限創建分支指令。
    @Override
    public void registerCommand(CommandRegistry<CommandSender> commandRegistry) {
        commandRegistry.command(TestCommand.class, c -> {

            c.command(TestSayCommand.class);

            c.command(TestCalculateCommand.class, cc -> {

                cc.command(TestCalculateAddCommand.class);

                cc.command(TestCalculateMinusCommand.class);

            });

            c.command(TestConfigCommand.class, cc -> {

                cc.command(TestConfigCheckCommand.class);

                cc.command(TestConfigEditCommand.class);

                cc.command(TestConfigReloadCommand.class);

            });

            c.command(TestServiceCommand.class, cc -> {

                cc.command(TestServiceByeCommand.class);

                cc.command(TestServiceHelloCommand.class);

            });

            c.command(TestSchedulerCommand.class, cc ->{

                cc.command(TestSchedulerOneCommand.class);

                cc.command(TestSchedulerTwoCommand.class);

            });
        });
    }

    @Override
    public void registerListeners(ListenerRegistry<Listener> listenerRegistry) {
    }


}

Command Arguments

Here are the calculation commands

@Commander(
        name = "calculate",
        description = "test calculate command",
        alias = {"cal", "c"}
)
public class TestCalculateCommand implements CommandNode {

    @Override
    public void execute(CommandSender commandSender) {
    }
}
@Commander(
        name = "add",
        description = "calculate add command",
        alias = {"ad", "plus", "a"}
)
public class TestCalculateAddCommand implements CommandNode {

    @CommandArg(order = 0)
    private int one;

    @CommandArg(order = 1)
    private int two;

    @Override
    public void execute(CommandSender commandSender) {
        commandSender.sendMessage(one+" + "+two+" = "+(one + two));
    }
}
@Commander(
        name = "minus",
        description = "minus command",
        alias = {"reduce", "m"}
)
public class TestCalculateMinusCommand implements CommandNode {

    @CommandArg(order = 0)
    private int one;

    @CommandArg(order = 1)
    private int two;

    @Override
    public void execute(CommandSender commandSender) {
        commandSender.sendMessage(one+" - "+two+" = "+(one - two));
    }
}

the usage is shown below:

/calculate add <one> <two>
/calculate minus <one> <two>

The command API has parsed your string argument into the type that you have declared from @CommandArg automatically.

Now, let's do some changes for those two @CommandArg

    @CommandArg(order = 0, labels = {"first value"})
    private int one;

    @CommandArg(order = 1, labels = {"second value"}, optional = true)
    private int two = 22;

After that, the usage will become

/calculate add <first value> [second value]
/calculate minus <first value> [second value]

What's changed?

  • after setting the label, the usage display label changed.
  • the second argument becomes optional, if you don't input it, it will use the default value 22

Get the Remaining Args

Getting remain args is simple:

@Commander(
        name = "add",
        description = "calculate add command",
        alias = {"ad", "plus", "a"}
)
public class TestCalculateAddCommand implements CommandNode {

    @CommandArg(order = 0)
    private int one;

    @CommandArg(order = 1)
    private int two;

    @RemainArgs
    private List<String> args;

    @Override
    public void execute(CommandSender commandSender) {
        commandSender.sendMessage(one+" + "+two+" = "+(one + two));
        commandSender.sendMessage("remainArgs: "+args.toString());
    }
}

so when you execute /calculate add 1 1 a b c d e the remaining args will be [a, b, c, d, e]

Register your Own Command Arguments

just go to the main class:

(For spigot)

@ELDBukkit(
        registry = TesterRegistry.class,
        lifeCycle = TesterLifeCycle.class
)
public class ELDTester extends ELDBukkitPlugin {

    @Override
    public void bindServices(ServiceCollection serviceCollection) {
    }

    @Override
    protected void manageProvider(BukkitManagerProvider provider) {
        var parser = provider.getArgumentManager(); //參數解析器
        // 創建參數解析
        parser.registerParser(Integer.class, (iterator, commandSender, argParser) -> {
            try{
                return Integer.parseInt(iterator.next());
            }catch (NumberFormatException e){
                throw new ArgumentParseException("not a valid integer."); 
            }
        });
    }
}

(For Bungee)

@ELDBungee(
        registry = TesterRegistry.class,
        lifeCycle = TesterLifeCycle.class
)
public class ELDTester extends ELDBungeePlugin {

    @Override
    public void bindServices(ServiceCollection serviceCollection) {
    }

    @Override
    protected void manageProvider(BungeeManagerProvider provider) {
        var parser = provider.getArgumentManager(); //get the argument manager
        // create argument parser
        parser.registerParser(Integer.class, (iterator, commandSender, argParser) -> {
            try{
                return Integer.parseInt(iterator.next());
            }catch (NumberFormatException e){
                throw new ArgumentParseException("not a valid integer."); 
            }
        });
    }
}

the example shows the command argument parsing from string to integer so that you can use like:

@Commander(
        name = "add",
        description = "calculate add command",
        alias = {"ad", "plus", "a"}
)
public class TestCalculateAddCommand implements CommandNode {

    @CommandArg(order = 0)
    private int one;

    @CommandArg(order = 1)
    private int two;

    @Override
    public void execute(CommandSender commandSender) {
        commandSender.sendMessage(one+" + "+two+" = "+(one + two));
    }
}

It also supports register with identifier, for example:

        var parser = provider.getArgumentManager();
        // create parser with identifier 'message'
        parser.registerParser(String.class, "message", (iterator, commandSender, p) -> {
            StringBuilder builder = new StringBuilder();
            iterator.forEachRemaining(s -> builder.append(s).append(" "));
            return builder.toString();
        });

so if you mark the identifier as message in your @CommandArg, it will do the above parsing instead of just passing one string argument.

Example usage:

@Commander(
        name = "say",
        description = "test say command",
        alias = {"sa", "s"}
)
public class TestSayCommand implements CommandNode {

    @CommandArg(order = 0, identifier = "message")
    private String message;

    @Override
    public void execute(CommandSender commandSender) {
        Bukkit.broadcastMessage(commandSender.getName()+" says: "+message);
    }
}

you can also use the argument parser inside an argument parser:

argumentManager.registerParser(Location.class, (args, sender, parser) -> {
            World world;
            if (!(sender instanceof Player)) {
                world = Bukkit.getWorld(args.next());
            } else {
                world = ((Player) sender).getWorld();
            }
            if (world == null) {
                throw new ArgumentParseException("&cunknown world");
            }
            var x = parser.tryParse(Double.class, args, sender); //internal parsing
            var y = parser.tryParse(Double.class, args, sender); //internal parsing
            var z = parser.tryParse(Double.class, args, sender); //internal parsing
            return new Location(world, x, y, z);
        });

Parse A Dynamic Arg

@Commander(
        name = "sleep",
        description = "sleep command"
)
public class TestSleepCommand implements CommandNode {


    // this argument have long and string two types
    // the type must be Object
    @DynamicArg(order = 0, types = { Long.class, String.class })
    private Object seconds;

    @Inject
    private ScheduleService scheduleService;

    @Override
    public void execute(CommandSender commandSender) {
        if (seconds instanceof Long){
            commandSender.sendMessage("sleeping...");
            var time = (Long) seconds;
            scheduleService.injectTask(new BukkitRunnable() {
                @Override
                public void run() {
                    commandSender.sendMessage("you wake up!");
                    commandSender.sendMessage("you just slept "+seconds+" seconds!");
                }
            }).timeout(time * 20L).run(ELDTester.getPlugin(ELDTester.class));
        }else if (seconds instanceof String){
            var time = (String) seconds;
            switch (time.toLowerCase()){
                case "never":
                    commandSender.sendMessage("you don't sleep ? okay you never sleep");
                    return;
                case "forever":
                    commandSender.sendMessage("you sleep forever ? that's not good!");
                    return;
                default:
                    commandSender.sendMessage("oh sorry what is "+time+" ? I don't understand.");
            }
        }
    }
}