JorelAli / CommandAPI

A Bukkit/Spigot API for the command UI introduced in Minecraft 1.13
https://commandapi.jorel.dev
MIT License
537 stars 67 forks source link

Annotation suggestions #163

Open JorelAli opened 3 years ago

JorelAli commented 3 years ago

Please describe your suggestion Annotation suggestions

Please supply examples of the desired inputs and outputs See misc/annotation-suggestions/ folder.

JorelAli commented 2 years ago

This is currently being worked on for the annotation update.

SzczurekYT commented 1 year ago

Is this still planned? Today I tried your api for the first time, using the annotations and I like them, would really appreciate this functionality.

JorelAli commented 1 year ago

Is this still planned? Today I tried your api for the first time, using the annotations and I like them, would really appreciate this functionality.

Yes, this feature is still planned! A formal specification of the new annotation system, along with the new suggestions format, is currently visible available to view here:

@Command("mycommand")
public class MyCommand {

    @AStringArgument
    @Suggests(ListOfNames.class)
    String name;

    @Executes
    public void myExecutor(Player player) {
        // ...
    }

    @Suggestion
    class ListOfNames implements Supplier<ArgumentSuggestions> {

        @Override
        public ArgumentSuggestions get() {
            return ArgumentSuggestions.strings("Player1", "Player2", "Player3");
        }

    }
}

We don't have a dedicated ETA for this feature, but we do have a dedicated milestone (10.0.0). Our original ETA was late August, but due to various reasons, our current outlook doesn't appear to be able to reach this deadline. I'm hoping to get some snapshot builds by August, but we could be looking towards October - December for some beta releases of this new annotation system.

SzczurekYT commented 1 year ago

Cool, than you for detailed response. But isn't creating an entire class and a separate field an overkill / too much boilerplate? Or is it possible to don't create the global argument?

For example I think something like this would be way simpler (inspired by the original spec misc/annotation-suggestions/ )

@Command("warp")    
public class WarpCommand {

    // List of warp names and their locations
    static Map<String, Location> warps = new HashMap<>();

    @Default
    @Arg(name = "warp", type = StringArgument.class, suggest = this::warpNames) // Suggestion reference here
    public void warp(Player player, String warpName) {
        player.teleport(warps.get(warpName));
    }

    @Suggestion
    public String[] warpNames() {
        return warps.keySet().toArray(new String[0]);
    }

}

Overall I not sure if this api based on the new spec isn't too complicated, but I just gave it a quick look and I would need to try it on a real case to see if it works for me or not. Also if told what to do I probably could contribute some code.

JorelAli commented 1 year ago
@Default
@Arg(name = "warp", type = StringArgument.class, suggest = this::warpNames) // Suggestion reference here
public void warp(Player player, String warpName) {
  player.teleport(warps.get(warpName));
}

Annotations can't use method references. Their values can only consist of primitives, Strings, Classes, Enums, Annotations or arrays of any of the preceding types.

It could be possible to use a string-based method reference, but I would rather avoid specifying method references by strings to allow users to have "referenced code" in their IDEs. For example, if you make a method warpNames() and a suggestion like this:

@Arg(name = "warp", type = StringArgument.class, suggest = "WarpCommand.warpNames")

If you then attempt to refactor the method in an IDE, it can't refactor the string, whereas if you used a direct reference (e.g. class reference), an IDE can refactor it properly. Of course, the spec is still in its infancy and suggestions are always welcome!

JorelAli commented 1 year ago

Or is it possible to don't create the global argument?

@SzczurekYT Yes, it's possible to not create global arguments. Global arguments are a feature, not a replacement for normal command arguments - they're designed to be shared across multiple command execution implementations. In the spec, there's an example of this:

@Command("mycommand")
public class MyCommand {

    @AStringArgument
    String name; // Global argument

    @Subcommand("subcommand")
    class Subcommand {

        @Executes
        public void myMethod(Player player, @AIntegerArgument int value) { // Local argument, still supported here
            // ...
        }

    }
}

isn't creating an entire class ... overkill / too much boilerplate?

Possibly, however it's worth noting that functional interfaces (classes that implement a @FunctionalInterface) is effectively no different than a lambda that does the same thing (there's a few minor bytecode optimizations and semantics, but the effective result is still identical). This implementation also lets you share suggestions across multiple command arguments.

SzczurekYT commented 1 year ago

When I was writing my previous comment I was a bit quick with judging, forgot about few things and overall I was a bit rude I think. Sorry for that. I agree with what you have said in your replies, and it definitely makes sense to do that the way you do it. I would need to spend some time (that I currently don't have) reading the entire spec to form more constructive opinion. but overall I think this is going to be a great api, and I want to thank you for what you already did, it's a huge time saver when you don't have to parse the command yourself and try to predict every stupid thing someone might type.

Edit: also I think you should change the for 9.0.0 annotations label to for 10.0.0 annotations so it's correct