1.16.5 Proper way to allow users to make addons for my plugin

Discussion in 'Spigot Plugin Development' started by Sigong, Jun 10, 2021.

  1. I have created a plugin that serializes a bunch of information about a player into a yml file, then "resets" the player to the state they were in when they joined the server for the first time (with the serialized information to be restored later). I had the thought that other plugins might have their own attributes or information for a player that a server using my plugin might also want to serialize and reset, so I've decided to create a way for users to create addons for my plugin that will allow them to add (and restore) their own information to (and from) the yml file.

    Today, I created a pair of plugins to test this sort of thing. Here is the procedure I followed:
    1. Create two plugins: BasePlugin and AddonPlugin
    2. Add BasePlugin as a depend in AddonPlugin's plugin.yml file
    3. Add the compiled jar of BasePlugin to AddonPlugin's pom.xml as a dependency so AddonPlugin can use classes from BasePlugin
    4. Get BasePlugin's instance using Bukkit.getPluginManager().getPlugin("BasePlugin") in AddonPlugin's onEnable(), then call a method in BasePlugin's main class.
    Everything seems to be working (the method that I called in BasePlugin sends a message to the console, which is being sent), but I'm not sure that I followed the proper procedure for adding BasePlugin as a dependency for AddonPlugin, and I'm not sure if a user who doesn't have the project on their computer could develop and addon using classes in BasePlugin (I don't really understand maven, I've just followed the tutorial on here and googled about how to include a jar file as a dependency.)

    Have I done this properly, and would a user be able to use the same process I used to develop their own addon (assuming all access modifiers were correct, etc)?

    Side Question: Is it possible to have a plugin depend on a specific version of another plugin in plugin.yml? If not, can this be done through maven? I could not find this information on the following page: https://www.spigotmc.org/wiki/plugin-yml/

    Code (Text):
            <dependency>
            <groupId>org.example</groupId>
                <artifactId>BasePlugin</artifactId>
                <scope>system</scope>
                <systemPath>C:/Users/REDACTED/IdeaProjects/BasePlugin/target/BasePlugin-1.0.jar</systemPath>
                <version>1.0.0</version>
            </dependency>
     
  2. I am not familiar with the system scope but it should work. Is the jar included within the new compiled jar of the add on? If not the system scope seems to be working as "provided" (see below). If so it will mess up your basePlugin classes if you use multiple addons that were not updated all to the same basePlugin version. I am not sure what version of the base plugin classes are loaded in the end there but I had to use relocation to solve the issue. You may look at my open source redye plugin alongside the commons code (also on my github). Look at the poms in the dist folders.

    Normally you would use the scope "provided" which means that the classes that your plugin depend on will be already provided by something else -> your base plugin which is loaded anyway. Keep in mind that you have to be careful about backwards compatibility if you change the version of your base plugin and for example delete/rename a method. Some addons may then throw NoSuchMethodExceptions.

    For your side question: I do not think this is possible but a workaround would be to check the version of the dependency in the code again after everything loaded and for example disable the plugin if the versions do not match.
     
  3. Using the service manager is the best way to interact with plugins. How you install the dependency doesn't really matter. You should only not compile it into the new jar you create. Could be gradle, maven, or even build artifacts. We just need access to the service you have registered. Take as an example VaultAPI and Vault . VaultAPI contains the classes the developer should have access to, while Vault is the internal implementation of these classes. After you implement them, you register them into the service manager. Again, an example of how to get access to a service can be found in VaultAPI's README file.

    What you've done in plugin.yml is correct: you need to add the BasePlugin as a dependency ( or soft dependency, if you don't want to require people to install your BasePlugin, best example is PlaceholderAPI: if the server has it: apply placeholders from it ; if not, don't ).
     
    • Like Like x 1
  4. I think the system scope is just like the provided scope, except the jar is linked to explicitly with a file path instead of being looked up in a repository (https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html). I'm using it because it seems to work, not because I think it's the correct way to do it or anything.
    I opened the AddonPlugin jar with winRAR and I don't see anything from BasePlugin.
    I looked at your pom file in the redye_dist folder on your github, but it was a lot of things that I have not seen before so I did not understand it.
    I tried changing the scope to provided, but I get an error that BasePlugin can't be found in the spigot repo (I think provided makes maven look for the dependencies in repos that are defined in the pom.xml, do I need to set up a repo to use it for a jar on my computer? Is there a better way to make one project depend on another that can be accomplished by users who only have access to BasePlugin compiled jar?).
    I'll have to think more about the version problem, are there resources anywhere (especially spigot-specific resources) on dealing with it?

    I looked into the servicesmanager and I'm not sure if it would work in my case, are there any good resources for learning to work with it?
     
  5. yeah then seems like it works as provided scope and just looks for a saved jar instead of a repo. Personally I use jitpack.io but it requires the source code to be published. I think in your case system scope is the proper way. (Although I am not 100% certain. If you dont mind try compiling your plugin, then move the basePlugin jar out of the original folder and then start your plugin. It should work in my understanding but as mentioned I did not work with system scope at all).

    Also what MrIvanPlays said: The absolute proper way would be definitely to wright an own API for your basePlugin. It depends if you want to put this much effort into it or not. As a matter of fact VaultAPI also uses jitpack. As you can see VaultAPI only contains abstract classes and interfaces which then get implemented by Vault. All of the source code is available so you should be able to get an understanding on how it works.

    /e forget everything I said about relocation. Relocation is important if multiple plugins depend on the same code from other repos that is included and compiled (not provided) the same way. Otherwise the shared code will only initiate once for all plugins using this code. For example in the beginning I used a very simple debug messages class which had only static methods and one debug boolean. I noticed that if I then switched on debug mode in one plugin in all of my other plugins debug mode was also activated.
     
  6. When you say "write an API" what exactly do you mean? Are a few public methods that let other plugins interact with yours an API (that was my plan before I started this thread)?
    My eventual goal is to have the BasePlugin contain an interface which AddonPlugin implements, then have AddonPlugin announce itself to BasePlugin somehow, then allow BasePlugin to call the interface methods in AddonPlugin when it needs to. I'm doing it this way because I want it to be possible to use multiple different addons (BasePlugin will put the AddonPlugin in a list once it announces itself, and then when it's time for the addons' methods to be called it will call the appropriate method from each addon in the list).
    @MrIvanPlays Would the services manager allow me to do the above? From what I've read about it it seems like only one plugin can provide a given service.

    EDIT: I have successfully used the servicesmanager to connect the two plugins and call methods in the addonPlugin, I also made a second addonPlugin and was able to call methods in both, so I think that that's sorted. I still would like to know about API's with respect to spigot specifically though.
     
    #6 Sigong, Jun 13, 2021
    Last edited: Jun 13, 2021
  7. The system scope is not what you want in maven. It is basically telling maven to ignore all the automation provides and to just accept what you give it. You will want to host the dependency in a maven repository. This can be a central one (maven central, jitpack.io, ...) or you can host one using github or any other file hosting platform.
    Great guide on hosting in github: https://gist.github.com/fernandezpablo85/03cf8b0cd2e7d8527063 *

    Your pom.xml file will look something like this:

    Code (Text):
    <repositories>
            <repository>
                <id>MmPlugins</id>
                <url>https://github.com/Mmaarten23/Maven/raw/master</url> <!-- Your hosting url -->
            </repository>
            ...
    </repositories>
    <dependencies
        <dependency>
            <groupId>com.mmaarten</groupId>
            <artifactId>MmCommands</artifactId>
            <version>1.0.0</version> <!-- Specify the version -->
        </dependency>
    </dependencies>
     
    This will allow maven to actually check if new versions are available rather than just including the specific jar you downloaded

    note: in the example given, there is no scope specified because my dependency gets shaded into the uber jar. If your baseplugin will be a plugin on the server, use <scope>provided</scope>
     
  8. Let's say I don't want to do that until the project is finished and ready to be uploaded as a resource to this site (I don't always finish the projects I start): are there any problems that could arise from switching the dependency from system scope to provided from a repository, then recompiling and uploading? I don't understand maven all that well.

    Side question: As you can see from my initial post, I have AddonPlugin's dependency's systemPath pointing right at BasePlugin's target directory, meaning that when I make a change in BasePlugin, the dependency is automatically updated with that change. It seems like changing the source of the dependency to some maven repo somewhere else would significantly slow down the process of developing the two plugins concurrently. Would you say that this is the case?
     
  9. There is a big difference between developing both plugins and using the base plugin as a dependency as an "outsider".

    If you really are working on both plugins at once, you can use the system scope but that is very unmaintainable. For example: in a couple months, you decide to clean up your pc and you move the folder that the dependency jar is in, one folder up in the directory tree. The direct path to your dependency is now no longer valid and when you want to work on your addon plugin, you will have to figure out what the new path is.

    Maven is there to accommodate that so yes, I would suggest hosting it in a maven repo even while developing. If you do not want to deal with having to upload/download dependencies all the time, you can install the dependency jar in the local maven repository which can be found in your home directory -> Users -> [your user] -> .m2 -> repository. To install a file here, run the following script in a command line (windows):

    mvn install:install-file -Dfile=<path-to-file> -DgroupId=<group-id> -DartifactId=<artifact-id> -Dversion=<version> -Dpackaging=jar



    Whether you put system scope there or not does not actually matter for people who will be writing addons for your plugin as depending on your base plugin is the responsibility of the addon. It is just a really bad idea to put the system scope in your example.

    When it comes to publishing this resource:
    You will be publishing the means to get this resource through maven. You wont upload a jar to this site. If you want to take a look at how I am hosting my MmCommands dependency, you can check that out here:
    Source code: https://github.com/Mmaarten23/MmCommands
    Maven repo: https://github.com/Mmaarten23/Maven
    Spigot resource page: https://www.spigotmc.org/resources/mmcommands.92626/
    Github wiki page about how the dependency works: https://github.com/Mmaarten23/MmCommands/wiki/Getting-started