1.8.8 Making Plugin to work from 1.8 to 1.15.2

Discussion in 'Spigot Plugin Development' started by PvPNiK, Feb 17, 2020.

  1. Hello,
    how do I make a plugin that will work on all the versions?
    what exactly stops the plugin from working on different versions? (not talking about NMS).

    if I will use spigot's 1.8 API and test the plugin on 1.15.2 server, does it mean it will work on all the versions between them too?
     
    • Friendly Friendly x 1
  2. SteelPhoenix

    Moderator

    Most likely yes
     
    • Like Like x 1
  3. The biggest thing you will want to look at is the API difference between 1.8 and 1.15, I think there are a few methods that have changed drastically like getting the item in the player's hand for example. You will definitely want to test it, but I think you will want to test from 1.8 up, instead of the opposite way. I could be very wrong, another thing you could do (which is time consuming) is just make multiple versions of the plugin, just using the different API-versions for spigot.

    Feel free to correct me if I am wrong, I am also fairly new to this, so if there is an easier way, it would be nice to know.
     
  4. Well yes but actually no. The biggest obstacle you'll run into is the Material flattening of 1.13. Also several APIs are missing from older versions, such as particles and attribute modifiers in 1.8. Additionally, several method signatures have either been deprecated or replaced entirely between the range of versions.

    What I personally do is make a separate gradle module to import multiple versions of the Spigot API and use a single version check to determine which module to use. Only the parts that differ between versions need to be part of a separate module.
     
  5. Here is an example:
    After the combat update, the PlayerInteractEvent will fire twice, 1 time for each hand.
    But in 1.8 there wasn't any second hand, so if the server on 1.15.2 it will fire the event twice? and if so how could 1.8 plugins handle it?
     
    • Like Like x 1
  6. You should be able to check the server version and handle appropriately. Something like:
    Code (Java):
    Bukkit.getVersion()
    With this you could use a boolean to see which version the server is:

    Code (Java):
    Boolean version = Bukkit.getVersion().contains("1.x.x");
    Or just a straight up if/else or switch:

    Code (Java):
    if(Bukkit.getVersion().contains("1.x.x){
        Do something;
    }
    This should allow you to at the very least check what version the server is using, and then choose how to handle the main hand and off hand events for the versions that contain them, and then for the ones that don't, you can figure out how to handle the events for the hand. I am not too sure exactly how you would go about that, but I think this could help as a stepping stone for it.
     
    • Like Like x 1
  7. yea but I am using 1.8 API when coding the plugin.
    and it does not have any methods for the hands.
    (there is no PlayerInteractEvent#getHand(); ).
    so if I am using 1.8 API that doest have it.
    how can I disable the event from firing twice? (there isn't a way to check which hand clicked)

    The plugin coded using 1.8 API, the server in on 1.15.2.
    the server will fire the event twice, so how can I make it to fire once and not twice?
    (after the Combat update, we are disabling the second event fire by eliminating it with: "if (PlayerInteractEvent.getHand() != EquipmentSlot.HAND) return;")

    and if I will use 1.15.2 API for the code, how I can make sure I am using all methods from 1.8, and not new ones which the 1.8 API does have?
     
  8. You can't without using reflection if you're using the 1.8 API. You should be using the 1.15.2 API to compile so you can access everything properly. Use version checking to avoid running code that will fail in certain versions.

    Here's a little snippet:
    Code (Java):
    @EventHandler
    public void onInteract(PlayerInteractEvent event) {
        if (bukkitVersion > 1.8 && event.getHand() == EquipmentSlot.OFF_HAND) { // do the version check yourself
            // do things here assuming off hand usage
        } else {
            // do things here assuming main hand usage
        }
    }
     
  9. See, this part I think is the biggest issue you will run into, and honestly, the only answer I can come up with logically is to cross check with the API's and make sure that when you do the version checking you use methods specifically from that specific API version.

    So taking into consideration what Esophose said:

    Code (Java):
    @EventHandler
    public void onInteract(PlayerInteractEvent event) {
        if (bukkitVersion > 1.8 && event.getHand() == EquipmentSlot.OFF_HAND) { // do the version check yourself
            // do things here assuming off hand usage
    //Use the 1.15 api for this
        } else {
            // do things here assuming main hand usage

    //and the 1.8 api for this
        }
    }

    ***I do not know if this works, I have never tried to make a plugin for such a large gap between server versions.
     
    #9 FyrenGaming, Feb 18, 2020
    Last edited: Feb 18, 2020

  10. .... what does this have to do with the original post?
     
  11. Somehow my answer on another post ended up here? It's just a mistake, forget about it
     
  12. Ah, okay :)
     
  13. For example, I am using 1.15.2 API, is there any easy way to know when I am using methods/references that 1.8 API doesn't have?
     

  14. Honestly? I don't believe so, I think you will just have to cross check the APIs and just make sure you do a lot of testing.
     
  15. "Cross" compiling replace the version in your pom xml and try to compile it. When it fails cause of ClassNotFoundException something is not present.
     
  16. drives_a_ford

    Moderator

    Change the dependency in your IDE. If things light up as incorrect (red in most cases), then you definitely have issues. You can then try to compile to see if the compiler finds issues.

    If you truly want to support multiple versions with different handling of certain events (or API in general), you can always use abstraction. You have an interface(s) (or an abstract class if you need to) that defines the needed methods. You then create different modules/projects for certain version specific things which have implementations for the interfaces. Each version specific module or project (they're named modules in gradle and projects in maven) can depend on the specific version of the API that is needed. And when your plugin starts (or when the class instance is first needed), you check the server version and initialize the implementation from the correct module/project.
     
    • Agree Agree x 1
  17. Also, the XSeries might be helpful. Some specific cases will however require you to utilize a multi module project structure (See: https://github.com/sainttx/Holograms) - by abstracting an interface you can handle logic according to version. This is not limited to NMS, but most commonly seen there. Another important aspect is that, so long you do not import, branching execution will not fire an exception.

    Example, if you compile against 1.15.2 you can just use the following logic:

    IF VERSION IS 1.9 OR ABOVE:
    ACCESS METHOD ONLY AVAILABLE ON 1.9
    HANDLE LOGIC ACCORDINGLY
    ELSE
    HANDLE LOGIC ACCORDINGLY

    Then by compiling against 1.9 you have access to the new method, but when playing on 1.8 you would not enter the branch for 1.9 hence no errors would be fired. Avoid imports that do not exist on different versions and use fully annotated paths and it should run like that.
     
  18. i have problem with spawning armor stand.
    i am using 1.15.2 api.
    on 1.15.2 the code works fine however on 1.8 server the armor stand is not spawning.
    Code (Java):
        public static ArmorStand getNewArmorStand(Location location) {
            ArmorStand as = location.getWorld().spawn(location, ArmorStand.class);

            as.setVisible(true);
            if (!isOneHandedVersion())
                as.setInvulnerable(true);

            as.setCustomName(WeaponHolder.getInstance().getDescription().getName());
            as.setCustomNameVisible(true);

            as.setBasePlate(true);
            as.setArms(true);
            as.setCanPickupItems(false);
            as.setGravity(false);
            as.setRightArmPose(new EulerAngle(Math.toRadians(90), 0, 0));
            return as;
        }
    And:
    Code (Java):
    public void spawn() {
            Location positionLocation = position.getLocation();

            Material material = itemStack.getType();
            if (WeaponHolder.getInstance().itemPositionManager.contains(material)) {
                ItemPosition itemPosition = WeaponHolder.getInstance().itemPositionManager.get(material);
                if (itemPosition.hasLocation(position))
                    positionLocation = itemPosition.getLocation(position);
            }

            Location blockLocation = getHolderLocation().clone();
            blockLocation.add(positionLocation);
            blockLocation.setYaw(position.getYaw());
            blockLocation.setPitch(position.getPitch());

            Bukkit.broadcastMessage("loc: " + blockLocation);
            armorStand = Utils.getNewArmorStand(blockLocation);
            Bukkit.broadcastMessage("armorStand: " + armorStand);
            if (Utils.isOneHandedVersion()) {
                Bukkit.broadcastMessage("Utils.isOneHandedVersion())");
                armorStand.setItemInHand(itemStack);
            }
            else
                armorStand.getEquipment().setItemInMainHand(itemStack);
            Bukkit.broadcastMessage("getEquipment: " + armorStand.getEquipment().getItemInHand());
        }
     
  19. Did armor stands exist back then? Any error?
    Also, in 1.8, there's no "main hand"
     
  20. armor stands were added in 1.8, no errors.