[SPIGOT-5658] Allow multiple api-versions to be listed in plugin.yml Created: 05/Apr/20  Updated: 29/Mar/21

Status: Open
Project: Spigot
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: New Feature Priority: Minor
Reporter: Ben Woodworth Assignee: Unassigned
Resolution: Unresolved Votes: 1
Labels: None

Version: CraftBukkit 1.15
Plugin: FastCraft
Guidelines Read: Yes

 Description   

At the moment it's only possible to specify one api-version in the plugin.yml, but it is possible to support and build against multiple API versions in a single plugin.

For my plugin FastCraft, I support CraftBukkit 1.7-1.15+. For breaking API changes, I have different dependencies I inject (compiled against different API versions), and when the plugin starts it chooses between them based on the server's API version.

I currently use `api-version: 1.13`, since that allows any current server version to use it. But, if there's ever a new version that drops backwards compatibility again, it'll be impossible to let my plugin work on new and old servers (even if it is able to).

Bukkit PR: https://hub.spigotmc.org/stash/projects/SPIGOT/repos/bukkit/pull-requests/594/overview



 Comments   
Comment by Ben Woodworth [ 27/Oct/20 ]

@md_5 I forgot to reply, but I did a deep dive into the bytecode modifications being done, and came to the conclusion that they (or any modifications added in the future) shouldn't be a problem.

If a plugin has multiple API versions listed in its plugin.yml, the server can select the highest of them that it supports (e.g. matching against CraftMagicNumbers.SUPPORTED_API), and make bytecode modifications to all of the plugin's classes, safely assuming that they all work with the selected API version.

It's a safe assumption because the plugin will only be using classes that work with that API (version-based dependency injection, etc.). Bad modifications might be made to the other classes, but that won't matter since they'll never be loaded/used anyway.

I implemented an api-versions entry for the plugin.yml, with CraftBukkit picking the latest version that it supports. (Bukkit: kepler_/api-versions, CraftBukkit: kepler_/api-versions)

In my opinion, if a dev knows that their plugin works across multiple API versions (e.g. they add support for them, they only use APIs that didn't break, etc.), they should be able to note that in the plugin.yml and let the plugin be used. It makes development easier (only need to publish a single jar), and it makes it easier for the server owners since there is only one jar to choose from. A warning in the documentation for api-versions could definitely be useful, but compiling against multiple API versions isn't that uncommon, and it seems needlessly restrictive to disallow

Comment by Toshimichi [ 27/Oct/20 ]

I think an option to disable bytecode modification is useful. (Or at least to try to load the plugin without bytecode modification when api-version is provided but invalid)

For example, FastAsyncWorldEdit has a great wrapper for every NMS version and one single jar could potentially support all Minecraft versions if it had not been for api-version option (You know WorldEdit/FastAsyncWorldEdit already has a wrapper for Bukkit API).

My plugin currently has a multiple-version support for NMS just like below but the existence of api-version is pretty annoying.

package net.toshimichi.dungeons.nat.v1_15_2;

import net.toshimichi.dungeons.nat.api.CooldownUtils;
import net.toshimichi.dungeons.nat.api.Installer;
import net.toshimichi.dungeons.nat.api.LocaleLanguage;
import net.toshimichi.dungeons.nat.api.NbtItemStackFactory;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.ServicePriority;
import org.bukkit.plugin.ServicesManager;

public class NativeInstaller implements Installer {
    @Override
    public boolean isAvailable() {
        try {
            Class.forName("org.bukkit.craftbukkit.v1_15_R1.CraftServer");
        } catch (ClassNotFoundException e) {
            return false;
        }
        return true;
    }

    @Override
    public void install(ServicesManager manager, Plugin plugin) {
        manager.register(LocaleLanguage.class, new NativeLocaleLanguage(), plugin, ServicePriority.High);
        manager.register(NbtItemStackFactory.class, new NativeNbtItemStackFactory(), plugin, ServicePriority.High);
        manager.register(CooldownUtils.class, new NativeCooldownUtils(), plugin, ServicePriority.High);
    }
}

injection.yaml

native: 
 - net.toshimichi.dungeons.nat.v1_15_2.NativeInstaller
 - net.toshimichi.dungeons.nat.v1_14_4.NativeInstaller
 - net.toshimichi.dungeons.nat.reflection.NativeInstaller

 

Comment by md_5 [ 05/Apr/20 ]

Yes, but even though the current modifications just fall into two boxes (pre/post 1.1.3) there is no reason why it will stay like that in the future.

https://hub.spigotmc.org/stash/projects/SPIGOT/repos/craftbukkit/browse/src/main/java/org/bukkit/craftbukkit/util/Commodore.java

Comment by Ben Woodworth [ 05/Apr/20 ]

Is there bytecode modification done on a plugin if its api-version is the same as the server's?

And could you point me to where bytecode modifications are done in the repos? I'm interested in looking into it and seeing how it works.

Comment by md_5 [ 05/Apr/20 ]

I don't think this is possible, and its not the way the Bukkit API was intended to use.
The api-version results in bytecode modifications being done to your plugin.

The server has no way to know which classes are for which version and how to modify the bytecode accordingly.

Generated at Wed Apr 02 10:57:20 UTC 2025 using Jira 10.3.3#10030003-sha1:d220e3fefc8dfc6d47f522d3b9a20c1455e12b7b.