Creating an API?

Discussion in 'Plugin Development' started by ChrizC, Jun 18, 2011.

Thread Status:
Not open for further replies.
  1. Offline

    ChrizC

    Hey guys,

    is there a tutorial out there on how to create an API? I've tried looking at a couple of sources that do this, and it makes no sense.

    Chris :cool:
     
  2. Offline

    garbagemule

    Hey Chris,

    It depends on what you're trying to create an API for, but in general, you provide a list of methods that can or must be implemented by the users of your API, and then you make sure to call those methods when appropriate. The MobArena API is very, very simple, as it involves only two really small classes (this and this).

    The Handler-class is basically just a bunch of get-methods, which means if someone creates a MobArenaHandler object in their plugin, they will be able to run a couple of checks on the plugin to create additional functionality.

    The Listener-class is just a bunch of empty methods. The point of this class is to allow users of the API to extend the Listener, and implement only the methods they need. Another way to do it would be to create an interface or an abstract class, but that would require them to possibly have to implement all the methods. After implementation, it works (pretty much) just like the listeners you're used to from Bukkit. When constructed, the listener adds itself to the listener-list in the ArenaManager. The magic then happens in the ArenaManager, where the arenaStart()-method, for instance, loops through each listener and calls its onArenaStart()-method.

    I hope this was helpful, otherwise, take some time to maybe read up on the Observer design pattern (it's very well described in the book I've mentioned before).
     
  3. Offline

    Randy Schouten

    I too am very much in need to learn how to create an API.

    I kind of understand how they work, with the "return [something]", but one of the things I do not understand is how to create the syntax.
    For instance, lets say someone typed a command which triggered a method from my plugin.
    How would my API know who to send a message to?

    I've taken a look at a few APIs and I saw the syntax simply had "Player player" in them.
    Now this is where my confusion is. How does it get the player?
     
  4. Offline

    garbagemule

    Take a look at this UML diagram of the Observer Pattern: http://en.wikipedia.org/wiki/File:Observer.svg

    The API is the "Observer" class in this case. The observer is (sometimes) an abstract class, meaning it has methods that concrete observers (classes that extend the Observer class) must implement (in this case the notify()-method). The "Subject" is your plugin. As you can see, the subject holds an observerCollection, which is basically a list/set/other collection of observers. In the constructor of the abstract observer class, you add the observer instance itself to the collection in the subject.

    Example: Your plugin, FooBar, holds a List<FooObserver> fooObservers. If this List is protected or public (not good programming practice, though), in your FooObserver class' constructor, you specify FooBar.fooObservers.add(this) (if fooObservers is static, otherwise you'll have to get the plugin instance). At this point in time, you know that if any class extends FooObserver, it will be added to the list on creation. Then, if notify() is the method you want to call on the observers, in your plugin, you simply call for (FooObserver observer : fooObservers) observer.notify(); Still confusing? Here's some code examples:

    Main plugin, FooBar:
    Code:java
    1. public class FooBar extends JavaPlugin
    2. {
    3. protected static List<FooObserver> fooObservers = new LinkedList<FooObserver>();
    4. ...
    5. public void notifyObservers()
    6. {
    7. for (FooObserver observer : fooObservers)
    8. observer.notify();
    9. }
    10. }

    FooObserver:
    Code:java
    1. public abstract class FooObserver
    2. {
    3. public FooObserver()
    4. {
    5. FooBar.fooObservers.add(this);
    6. }
    7.  
    8. public abstract void notify();
    9. }

    Now, whenever you call the method notifyObservers() in your main plugin, the notify()-method of any class that extends FooObserver will be called. Since notify() is abstract, you require any class that extends FooObserver to implement this method. You can also leave out the abstract keyword completely, and then give the method an empty body. In that case, classes that extend FooObserver won't have to implement the method - this is useful if you have a tonne of methods that are specific to certain applications, and thus don't need to be implemented in all classes that extend your observer class.
     
  5. Offline

    Randy Schouten

    That is quite a lot of information I do not really understand, to be honest.

    I want to get a int from the other plugin I'm creating.
    It's a quest plugin, and so I want the player to get the quest, for which I need the player who typed a command.

    So the thing I'm going to need is the player who executed the command.

    I'll just write down as I have it now.

    Code:
    public class eqAPI {
        public void getCurrentQuest(Player player){
            //Get int from hashmap
            Integer.parseInt(eq.questcurrent.get(player).toString());
        }
    }
    Are people able to call this from their plugins now?
    I'm terribly sorry if I sound extremely dumb, but I haven't had any more advanced things in Java :p
     
  6. Offline

    garbagemule

    It's all part of the learning process, don't worry! It's better to ask twice. What you're requesting is not quite the Observer Pattern and listeners so much as a "handler" class. If you want people to be able to use that method, all they need to do is create a new eqAPI(), and then call getCurrentQuest(p) (where p is a Player object). However, "eq" and "questcurrent" are not defined anywhere here, so you must make sure to do that.

    The MobArenaHandler class references static methods in the ArenaManager class. If these methods weren't static, the only thing I would have to do, would be to get the MobArena instance through the PluginManager, and assign it to a field in MobArenaHandler, and then instead of calling ArenaManager, I would call the instance.
     
  7. Offline

    oliverw92

    This isn't really a proper API, but it might help you with your problem, Randy. I'll use my DataLog plugin as an example.

    This is my 'API' class: https://github.com/oliverw92/DataLog/blob/master/src/uk/co/oliwali/DataLog/util/DataLogAPI.java . It allows other plugins to log information using my plugin (not much there at the moment i know).

    In its most basic form, another plugin can use methods in my API by first importing uk.co.oliwali.DataLog.util.DataLogAPI and then simply using this static method: DataLogAPI.addEntry(JavaPlugin plugin, String action, Player player, Location location, String data);

    So for example, if you wanted to log when someone goes home in your Home plugin:

    Code:
    private void goHome(Player player, Home home) {
        Location loc = home.getLocation();
        player.teleport(loc);
        DataLogAPI.addEntry(this, "Go Home", player, loc, home.getId());
    }
    The issue with that is, what if the server owner doesn't have DataLog, but they want the Home plugin? When the server owner starts their server, you'll get a nice error about there being no DataLogAPI class. You can get around this by checking if DataLog is actually loaded when you start the plugin:

    Code:
    public class MultiHome extends JavaPlugin {
    
        public boolean usingDataLog = false;
    
        public void onEnable() {
            Plugin dl = getServer().getPluginManager().getPlugin("DataLog");
            if (dl != null)
                this.usingDataLog = true;
        }
    
        private void goHome(Player player, Home home) {
        Location loc = home.getLocation();
        player.teleport(loc);
        if (this.usingDataLog) DataLogAPI.addEntry(this, "Go Home", player, loc, home.getId());
    }

    In the onEnable method, I check if DataLog is loaded, if it is then i set my usingDataLog boolean to true. I then check that boolean at any location where I use the API.

    This is only one 'idea' of an API, there are better and more complex ones however it sounds like this might be all you need.
     
  8. Offline

    Shamebot

    @garbagemuleWouldn't a interface instead an abstract class be better? You couldn't provide a constructor but...
     
  9. Offline

    garbagemule

    @Shamebot - With an interface in the Observer Pattern, you force the API user to manually add their observer to the collection of observers. This is how it works with the PluginManager for Bukkit, but it also makes more sense there, because one listener can catch multiple types of events. In these fairly simple examples, I think it would be overkill. MobArenaListener is not abstract nor an interface, because with abstract classes, I force the API user to implement all the methods, even if they don't need them. A more elegant and generically correct way to do it would be to provide an interface as well as the listener that I have right now (which would then implement the interface), but again, it's a bit overkill for something this simple.

    Interfaces and abstract classes are only really necessary if there are methods that the API users MUST implement - such as onEnable() and onDisable in the Bukkit JavaPlugin class, where these methods act as constructors and destructors, respectively, for plugins.

    Edit: I'd also like to add that when speaking in more generic and "good practice" terms, fields should be kept private whenever possible, and accessed and modified only through accessors and mutators respectively. Much like the PluginManager and its registerEvent() method.
     
  10. Offline

    Shamebot

    @garbagemule I suggested an interface so you can still extend another class, but yeah it isn't really necessary, because you simply can use the observer as an inner class.
     
  11. Offline

    garbagemule

  12. Offline

    Randy Schouten

    :confused:

    I have no idea what you guys are talking about. :p
    The only thing I need at this moment is getting something from a hash map and send it to the other plugin.
    It's a quest plugin and I want to link it with my other plugin called NPC-Quester. But since uQuest is getting outdated, I figured I'd create my own quest plugin.
    So I want to keep NPC-Quester seperated because not everyone has NPCs.
    Therefor I need a very simple API to get the quest NPQ-Quester has to give to the player.

    I hope this cleared up what I need a little bit :p
     
  13. Offline

    yakasuki

    Halp! Iz Wet In Heer
     
Thread Status:
Not open for further replies.

Share This Page