Proper command handling for Plugins - API 3.x.x

Discussion in 'Development' started by Zorlac, Sep 7, 2019.

  1. Zorlac

    Zorlac Spider

    Messages:
    10
    Hey @dktapps or @SOFe

    The problem I'm seeing here (IMHO) is that there are TONS of references, most of which are out of date and no longer work due to changes in the API. The example in the ExamplePlugin helps a bit, but seems a bit incomplete for handling typical usage of commands (permissions, checking args, etc.). Other examples I've seen people here refer to when answering questions about command handling are way in the weeds of complexity for your average "I wanna write a plugin" developer. (SkyBlock, for instance.)

    Keep in mind most of us are NOT the guys under the hook tinkering with the source code day in and day out, so the "just check the source code" answers lead to an endless path of trying to decipher what the hell is going on under there.

    The API is vastly improved from the old days and it is possible to write a decent, stable plugin without having to be a hardcore PHP developer with years of experience. That is, of course, assuming we know what we are dealing with. (I'm a total Pocketmine OOP noob and managed to muddle my way through writing a much needed plugin, but I did need to come here and ask for answers that I couldn't seem to find in the source code.)

    Here's a couple of examples.

    I've seen this in one plugin (in the main class):
    Code:
    class MainClass extends PluginBase{
    public function onCommand(CommandSender $sender, Command $command, string $label, array $args) : bool{
    
    and this in another (in a separate command/KeyCommand class):
    Code:
    class KeyCommand extends PluginCommand{
    public function execute(CommandSender $sender, string $commandLabel, array $args){
    
    Both seem to work fine, depending on the context and "extends" declaration of your class. However, the pros or cons of using one vs. the other are not entirely clear. I'm hoping you guys that live in the code could clear that up. I believe it would help a lot of people that want to write plugins and are confused about the correct way we are expected to use the API.

    Another confusion point is registering of the commands themselves so they work with autocomplete. I've seen them defined in the plugin.yml file, and then I've seen them in the command class file __construct. Poking around the source code, I can't quite determine the pros or cons for using either method.


    If either if you have some time, it would be very helpful to have a couple of examples of the different ways to properly use the API for handling commands with a brief explanation. Specifically I'm asking about when the command handling is done either inside or outside of the main plugin class file, such as in command/CommandName.php, along with the proper way to register those commands so they have tab completion and usage responses for incorrect syntax or # of args.
     
    Last edited: Sep 7, 2019
  2. SOFe

    SOFe Administrator Staff Member PMMP Team Poggit Admin

    Messages:
    1,910
    GitHub:
    sof3
    We've been meaning to get rid of the onCommand thing for a long time, but we haven't figured a nice and convenient alternative so far. Registering another class is too much hassle. PocketMine borrowed too much cargo cult API from Bukkit. Bukkit used an extends/implements pattern extensively in the API because it was written in the days before Java 1.8 lambdas, and somehow shoghicp wanted to copy them all into PocketMine.
    There are actually ambiguous things about the Command class, e.g. the lifetime of a Command instance (this matters because a class can store internal data, while a closure, although possible, shouldn't normally do so). But we have somehow all been brainwashed into thinking that OOP is better than passing closures (mainly because closures are not type struct in PHP, but we have libraries to solve that); even till today, some developers insist that an interface that only includes a called-once-immediately getter method and a called-once future function is better than directly passing the data and a closure for the future (see the discussion in my pull request about removing the Form interface). I call this OOP fetishism, and it's a pretty deep-rooted problem among plugin developers that we want to fix.

    As for the pros and cons of either approach right now, it's more like a personal choice. But the onCommand API has some maintainability problems that many developers would rather use the class-per-command approach (or even better, they build their own commands framework out of this one, but it's generally bad).
     
  3. SOFe

    SOFe Administrator Staff Member PMMP Team Poggit Admin

    Messages:
    1,910
    GitHub:
    sof3
    Yes you're right. But we don't have many people to write high quality documentation (especially on such a non-self-explanatory API), and we got a large codebase that takes forever to document.
    Usually, developers write docs while designing their API because they work like rubber duck debugging, but our whole API is a clone of the bukkit one, so apparently shoghicp just dismissed all problems he encountered with "let's see how bukkit is doing this and copy over better".
     
    dktapps likes this.
  4. Zorlac

    Zorlac Spider

    Messages:
    10
    Awesome, thanks for the feedback.

    Could you comment on the proper way to register commands? Which is preferred (plugin.yml or CommandMap)? Is one method going to eventually be deprecated?
     
  5. SOFe

    SOFe Administrator Staff Member PMMP Team Poggit Admin

    Messages:
    1,910
    GitHub:
    sof3
    The onCommand one is really unmaintainable and something we want to get rid of some day.
    But we also have a 3-year-old plan for a command full rewrite, which has never been finalized into an actual reviewable proposal (much less the code change) so far. So the command map one is also something we want to change, but expect much longer before that happens. (Maybe 5.0? 6.0? But our original ETA for 4.0.0 release was late 2018 and it's almost a whole year now!)
     

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.