Solved How does a plugin connects over the Spigot API to the server?

Discussion in 'Spigot Plugin Development' started by MrPyro13, Feb 13, 2020.

Thread Status:
Not open for further replies.
  1. Hey community,
    my question is maybe pretty easy, but I don't get it currently:
    I think, that the spigot API is used to develop plugins that run on the server. The server is the implementation of the Spigot API (https://www.spigotmc.org/threads/how-to-connect-the-api-to-the-implementation.249051/) and maybe something more.
    So why does the plugin knows, that it has to use e.g. the CraftPlayer object instead of the Player interface? Because when I develop plugins I use the API an there is only Player#xxx. Where is the connection between API and server (implementation)?

    Thanks for helping and sorry for my english :/
     
  2. I don't think I understand the question, but that's maybe just me.

    The Resource tag is not appropriate for this thread though, please remove it as this is not a resource.
     
  3. SteelPhoenix

    Moderator

    Spigot makes use of abstraction, which is a good practice.

    https://en.wikipedia.org/wiki/Liskov_substitution_principle

    In a nutshell:
    Code (Java):
    public interface Player {
        String getName();
    }
    public class CraftPlayer implements Player {
        @Override
        public final String getName() {
            return "MyPlayerName";
        }
    }

    // -------------
    Player player = new CraftPlayer();
    System.out.println(player.getName()); // Prints "MyPlayerName"
     
    • Agree Agree x 1
    • Winner Winner x 1
  4. Hi there

    This, in fact, isn't a question applicable only to Spigot, but to any API.
    You have to understand that when you use the API, you are using an interface (e.g. Player). An interface is an skeletton that, later, a class will implement. So you can't create "Player" instances, but a class that implements it, for example, CraftPlayer.

    To be simple:
    Code (Text):

    public interface Player {
         String getName();
    }

    public class CraftPlayer implements Player {
     
         private String name;
         public CraftPlayer(String name) {
             this.name = name;
         }

         @Override
         public String getName() {
             return this.name;
         }
    }
     
    As you see, the interface is only an skeletton that says to you that something that implements it will have a method "getName", but you don't see the code that actually returns the name.
    The implementation is done in CraftPlayer, which in this simple case returns the variable "name"


    When Spigot manages internally players, it is using CraftPlayer class. A implementation. But you can manage it as an interface (polymorphism)
    Code (Text):

    CraftPlayer player = new CraftPlayer("Dinnerbone");
    player.getName(); // returns "Dinnerbone"

    Player other = new CraftPlayer("Jeb");
    other.getName(); //return Jeb, and also works because you are accesing to the instance method
     
    In summary, when you are managing in your plugin "Player" is because you don't know (in the plugin) what implementation it is. Maybe is CraftPlayer, maybe not. The thing you know is that all the methods of the interface "Player" will work.
    When the code is executed in the server, it will probably be "CraftPlayer", but you don't need to know it.

    Similar to this is the Java List. List is an interface and can be implemented as an ArrayList, LinkedList... (each class has different algorithms and implementations), but you can declare variables as simple "List" because you probably will only use "get()" and "add()".

    Is common that a API methods returns an abstract interface, because you don't need to know the implementation. For instance, Server.getWorlds() return List<World>. You don't know if it is a LinkedList, ArrayList or anything else, and you don't need it.


    I hope i was clear :)

    Edit: There are already answers to the subject, I was writing it and I didnt see it hahaha
     
    • Winner Winner x 5
  5. An example to that could be the Player, every Player object on the server is actually a CraftPlayer (which implements Player) and therefore can be cast to Player.

    EDIT: Someone was quicker than me above
     
    • Like Like x 1
  6. OMG now everything makes sense, THANKS!
     
    • Funny Funny x 1
    • Friendly Friendly x 1
    • Like Like x 1
  7. Ok thanks a lot! It's now clearlyer...

    Now let's make a thought experiment. I want to develop an Inventory Builder plugin with API, to practice this logic.
    First I write an interface called "AbstractInventoryBuilder" with methods #addItem and #build. This is my API.
    Then I write an implementation of this called "MyInventoryBuilder" where I implement those methods. I compile my API as one file and the implementation (+ API) in another file, which is running as plugin on my server.
    Now I want another plugin (maybe a KitPlugin) to use my InventoryBuilder, so I put the API to my dependencies. But I can't use MyInventoryBuilder#addItem because the API don't provide these implementation, so I have to use AbstractInventoryBuilder#addItem.

    And there is a problem: I don't understand how this should work. How can the third program (KitPlugin) can access the implemented methods.
    I don't get the separation of API and implementation ‍♂️
     
  8. The API doesn't need to provide implementation. It simply states what the methods will take as parameters and what value it will return. The program who runs the API implements those methods itself (CraftPlayer implements all methods of the Player interface). You don't need to see the internals of CraftPlayer to see how it works, all you need to do is access the Player interface and it will run the implementation given by the 'hidden' CraftPlayer class. Something along the lines of encapsulation
     
    • Like Like x 1
  9. drives_a_ford

    Moderator

    It's sort of like when you talk to a random Human (interface) on the street. You assume they can talk (a method) and listen (another method). But you don't how they do it exactly. Most people will just use their ears and their mouth, but it doesn't really matter - they might be using a hearing aid and a brain computer interface for all you care - you just want to converse with them.
     
  10. its the same as spigot. you have the original plugin with both the implementation and the api. you have some way to access the api from within itself or from bukkit (service provider for example). you include the api as a library for your plugin. you have an original instance somewhere where you're getting a direct implemented instance of an interface somewhere, and from there, each interface returns other objects interface which have been implemented.
    impl -> interface -> other party views
    CraftServer -> Server
    CraftInventory -> Inventory
    CraftItemStack -> ItemStack
    Bukkit.getServer() returns an instance of CraftServer as a Server interface type. so you have a Server reference
    CraftServer#createInventory returns CraftInventory, which implements Inventory. Server#createInventory returns Inventory, which is implemented by CraftServer. calling Server#createInventory calls CraftServer#createInventory by technicality, because CraftServer implements Server, but Server returns Inventory type of CraftInventory when calling #createInventory. Server#createInventory returns CraftInventory implementation but as an Inventory type.
    CraftInventory#getItem returns CraftItemStack, which implements ItemStack. Inventory#getItem returns ItemStack. same as above.
    every interface has an implementation, but that implementation simply isnt visible depending on the library that you're using. this allows you to call the methods needed to interact with the plugin without modifying anything else that you generally dont need to be modifying. the MAIN perk (which is not generally used in most of bukkit simply bc of how its built) is using interfaces allow different implementations of the same thing. I could make a CustomCraftInventory (altho the way Bukkit/minecraft is designed, this isnt generally possible) that might do something different than CraftInventory would, but the 3rd party interacting with the api wouldn't know, they'd just be able to use it the same without issue. so every plugin interacting with inventories would have this new custom functionality, but their plugin would generally function as normal.
     
    • Winner Winner x 1
  11. [​IMG]

    So here is the thought experiment.
    API: Builder.java with #build() and InventoryBuilder with #addItem(ItemStack item)
    Implementation: MyInventoryBuilder.java that implements this two methods
    So now I want to use my API in the third party plugin. That's understandably not possible, because I can't access the MyInventoryBuilder object

    This example is similar to Spigot API, Spigot (server) and a Spigot Plugin, that is using only the Spigot API.
    I dont understand, how this can work...something is missing or am I slow today?
     
  12. So I need an instance that "transform" the API interfaces to the implemented ones @Warren1001 ?
     
  13. that could be a way of thinking about it.. but its not so much transforming as it is casting
    Code (Java):
    CraftServer implements Server {

        public CraftInventory createInventory() { // this and the below inventory method are the same method. since CraftInventory implements Inventory, you can change Inventory to CraftInventory as the method return.
            ...
        }

    }
    Server {

        Inventory createInventory();

    }
    {
        Server server = Bukkit.getServer(); // this basically calls 'return (Server)craftServer;'
        Inventory inventory = server.createInventory(); // this will call CraftServer#createInventory, but it gets returned as 'return (Inventory)craftInventory;'
    }
     
    • Like Like x 1
  14. But if I need an instance of an implementation, I can not separate API and implementation...
     
  15. if you are creating the instance yourself, you cannot do that. but something thats a utility class like an ItemBuilder, doesnt need type interfacing.

    /e the reason type interfacing is a thing is to allow other plugins access to large systems that can change easily. someone else can add their own implementation of something and every other plugin using the system will automatically start using it too. with something like an itembuilder, it simply doesnt work that way. you can certainly design it that way.. but it would defeat the entire purpose of the utility class.
     
  16. It was just an example that came to my mind to visualize my understanding problem...
     
  17. in that case, you CAN separate them, you just have to use the full plugin as the library (like we do when we import spigot instead of the spigot-api). the full plugin will have both implementation and the api, and you can cast the interface to the implementation. if you are creating your own instance, you dont use the interface, you use the implementation. and you wouldnt use an interface type unless you were creating a large system or wanted dynamic coding or wanted others to interact and implement their own ways of your system.

    type interfacing is used to get objects that already exist elsewhere. if you need to create your own object, thats not what type interfacing is designed for.
     
    • Like Like x 1
Thread Status:
Not open for further replies.