1.16.x Version independent materials?

Discussion in 'Spigot Plugin Development' started by CabbageRoll_, Nov 4, 2020.

  1. I have finally stepped out of 1.12.2 and managed to find out how to make sounds compatible between 1.8-1.16, for now and have no issues with that.
    First before that, what api version am i actually supposed to use? 1.8? 1.16? Something else? Currently still using 1.12.
    Next thing is blocks, i have no idea how to make itemstack/material version independent. I have the following code for example which works perfectly for 1.8-1.12.
    Code (Text):

    public static ItemStack[] blocks;

        static{
            if(Main.version.contains("1_8") || Main.version.contains("1_9") || Main.version.contains("1_10") || Main.version.contains("1_11")){
                blocks=new ItemStack[]{
                    new ItemStack(Material.WOOL, 1, (short) 14),
                    new ItemStack(Material.WOOL, 1, (short) 1),
                    new ItemStack(Material.WOOL, 1, (short) 4),
                    new ItemStack(Material.WOOL, 1, (short) 5),
                    new ItemStack(Material.WOOL, 1, (short) 3),
                    new ItemStack(Material.WOOL, 1, (short) 11),
                    new ItemStack(Material.WOOL, 1, (short) 10),
                    new ItemStack(Material.AIR),
                    new ItemStack(Material.WOOL, 1, (short) 8),
                    new ItemStack(Material.STAINED_GLASS, 1, (short) 14),
                    new ItemStack(Material.STAINED_GLASS, 1, (short) 1),
                    new ItemStack(Material.STAINED_GLASS, 1, (short) 4),
                    new ItemStack(Material.STAINED_GLASS, 1, (short) 5),
                    new ItemStack(Material.STAINED_GLASS, 1, (short) 3),
                    new ItemStack(Material.STAINED_GLASS, 1, (short) 11),
                    new ItemStack(Material.STAINED_GLASS, 1, (short) 10),
                };
            }else if(Main.version.contains("1_12")){
                blocks=new ItemStack[]{
                    new ItemStack(Material.CONCRETE, 1, (short) 14),
                    new ItemStack(Material.CONCRETE, 1, (short) 1),
                    new ItemStack(Material.CONCRETE, 1, (short) 4),
                    new ItemStack(Material.CONCRETE, 1, (short) 5),
                    new ItemStack(Material.CONCRETE, 1, (short) 3),
                    new ItemStack(Material.CONCRETE, 1, (short) 11),
                    new ItemStack(Material.CONCRETE, 1, (short) 10),
                    new ItemStack(Material.AIR),
                    new ItemStack(Material.CONCRETE, 1, (short) 8),
                    new ItemStack(Material.STAINED_GLASS, 1, (short) 14),
                    new ItemStack(Material.STAINED_GLASS, 1, (short) 1),
                    new ItemStack(Material.STAINED_GLASS, 1, (short) 4),
                    new ItemStack(Material.STAINED_GLASS, 1, (short) 5),
                    new ItemStack(Material.STAINED_GLASS, 1, (short) 3),
                    new ItemStack(Material.STAINED_GLASS, 1, (short) 11),
                    new ItemStack(Material.STAINED_GLASS, 1, (short) 10),
                };
            }else{
                //1.13+ version??
                //new ItemStack(Material.RED_CONCRETE),
                //new ItemStack(Material.YELLOW_CONCRETE),
                //...
            }
        }
     
    That would work but what now? I will have an error at the new code. If i use the newer api, the older part of code would have an error. How to make both compatible without having any deprecation issues? 1.13 does not have block/item data like <1.13 did.
     
  2. You might consider abstraction, but the best thing to do is probably use XMaterial. It pretty much handles most of that for you, outside of checking to see if concrete exists.
     
    • Useful Useful x 1
  3. I would rather not depend on external resources, and what would abstraction do that interface cannot? I know that putting a special method in interface is sufficient and then editing it based on version but for having blocks universal? (player can put custom blocks in gui and they get saved in a variable). Does that mean i would have to hardcode each single block for each version?
     
  4. XMaterial is designed to be copied into your plugin, it's just a single class file IIRC.
    Not sure what you mean? I mean like have an interface ItemLoader, and then three classes that implement that interface, like ItemLoaderLegacy, ItemLoaderLegacyConcrete, ItemLoaderConcrete. Then you can use the various methods of loading the items in separate class files for different versions. You'd still have to import a couple versions of the Spigot API but I find abstraction like that to be cleaner than an if block like that.
     
  5. Yep i have just seen it and sorry, its fine if its just a single file actually i immediately thought im getting recommended another bloated plugin that would make a massive performance and memory hog. Well still for research purposes... How could i manually do it, will the XMaterial code be sufficient to just look at?
    And about the interface i meant i have this package already:
    [​IMG]
     
  6. XMaterial does it with an enum that contains all previous names of each material, which you could certainly do.
    You could roll your item list into those, but you only have three versions of your item list so that might be a bit overkill. You'd also have to figure out how to not duplicate code between them. Up to you.
     
  7. The API version you should use is simply the lowest you're willing to support. If your plugin is relatively simplistic, you may find that 1.8 offers you a larger user base, whereas a more complex or modern resource might use 1.12 to ease development. Regardless, your concern about "deprecation issues" is unavoidable - anything pre-1.13 is certain to use outdated methods. For example, item durability/data is entirely deprecated, but you won't be able to form certain variations of pre-1.13 items without it.

    Please also note that, if you intend on supporting 1.13+ you must use api-version: 1.13 in your resource's plugin.yml file. This is necessary and simply states that the lowest "modern" version it will run on is 1.13; older versions will simply ignore the setting.

    I also highly recommend using XMaterial. There is no benefit to reinventing the wheel here. If you insist on an alternative, you could store the Material names as Strings and then load them as such, but it's far less intuitive. XMaterial is the way to go.
     
    • Agree Agree x 1
    • Useful Useful x 1
  8. I am not sure whether to choose 1.8 or 1.13 then. I want it to support 1.8-1.16 but use as least deprecated api functions as possible. (not quite sure how this is meant to work)
    1) will the plugin be able to interact with the new blocks/items and not be restricted to just 1.8? (if i choose 1.8)
    2) player.sendTitle method doesn't exist for 1.8 but exists for 1.12 api. Does that mean i can just reinvent it for the versions it doesn't support and for the ones that it does, just call the method from spigot api?
    And any further help on how should i decide?

    I have just seen the XMaterial but how do i actually copy that into my plugin? I believe its more than just a simple ctrl c ctrl v...
    Well i copy pasted it and im getting a warning and an error, what api version am i supposed to use again???
     
    #8 CabbageRoll_, Nov 4, 2020
    Last edited: Nov 4, 2020
  9. 1) The plugin will only be able to interact with the blocks/items present in whichever version of Minecraft the server is running.
    2) Yes, https://www.spigotmc.org/wiki/send-title-to-player-packets/ (or if you must, this then this)
    To help you decide, we would need to know what sort of plugin you're trying to make.

    I've only played with XSeries (including XMaterial) as a Maven dependency, but the Github page says it can be used independently. Afaik you should be able to copy/paste the contents of XMaterial.java and just change the package name. Note, however, you must leave the copyright notice as-is to comply with its MIT License.
     
  10. Well about the api version, again i want to support the blocks from 1.8-1.16 version so for now i have already put 1.13 api, and linked latest spigot (1.16.4) as referenced library. I don't use too much diverse api calls (title, inventory, scoreboard, playsound, sendblockchange are probably the only things im using the most) But now i have the problem with 1.13+ blocks, they get properly saved and put in a variable but won't appear in the world.
     
  11. I figured this out myself and after a few painful hours i managed to have the blocks load properly between versions. And i have set 1.13 api version.
    All the relevant code:
    Don't blame me on the code, i eventually do fix these verbose if statements when i am comfortable with it. If you got any suggestions please tell me.
    Code (Text):

    public class SendBlockChangeCustom {

        @SuppressWarnings("deprecation")
        public static void sendBlockChangeCustom(Player player, Location loc, int color){
            if(Main.version.contains("1_8") || Main.version.contains("1_9") || Main.version.contains("1_10") || Main.version.contains("1_11") || Main.version.contains("1_12")){
                player.sendBlockChange(loc, Table.blocks[color].getType(), Table.blocks[color].getData().getData());
            }else{
                player.sendBlockChange(loc, Table.blocks[color].getType().createBlockData());
            }
        }
     
        @SuppressWarnings("deprecation")
        public static void sendBlockChangeCustom(Player player, Location loc, Block block){
            if(Main.version.contains("1_8") || Main.version.contains("1_9") || Main.version.contains("1_10") || Main.version.contains("1_11") || Main.version.contains("1_12")){
                player.sendBlockChange(loc, block.getType(), block.getData());
            }else{
                player.sendBlockChange(loc, block.getBlockData());
            }
        }
     
     
    }
     
    Code (Text):

    public static ItemStack[] blocks=new ItemStack[]{
            XMaterial.RED_WOOL.parseItem(),
            XMaterial.ORANGE_WOOL.parseItem(),
            XMaterial.YELLOW_WOOL.parseItem(),
            XMaterial.LIME_WOOL.parseItem(),
            XMaterial.LIGHT_BLUE_WOOL.parseItem(),
            XMaterial.BLUE_WOOL.parseItem(),
            XMaterial.PURPLE_WOOL.parseItem(),
            XMaterial.AIR.parseItem(),
            XMaterial.LIGHT_GRAY_WOOL.parseItem(),
            XMaterial.RED_STAINED_GLASS.parseItem(),
            XMaterial.ORANGE_STAINED_GLASS.parseItem(),
            XMaterial.YELLOW_STAINED_GLASS.parseItem(),
            XMaterial.LIME_STAINED_GLASS.parseItem(),
            XMaterial.LIGHT_BLUE_STAINED_GLASS.parseItem(),
            XMaterial.BLUE_STAINED_GLASS.parseItem(),
            XMaterial.PURPLE_STAINED_GLASS.parseItem(),
        };
     

    Code (Text):

    //Still did not put XMaterial instead of Material
    Player player=(Player)e.getPlayer();
                Inventory inv=e.getInventory();
                //save blocks
                for(int i=0;i<7;i++){
                    if(inv.getItem(28+i)!=null){
                        Table.blocks[i]=inv.getItem(28+i);
                    }else{
                        Table.blocks[i]=new ItemStack(Material.AIR);
                    }
                }
             
                //save ghost
                for(int i=0;i<7;i++){
                    if(inv.getItem(37+i)!=null){
                        Table.blocks[i+9]=inv.getItem(37+i);
                    }else{
                        Table.blocks[i+9]=new ItemStack(Material.AIR);
                    }
                }
             
                //other
                if(inv.getItem(11)!=null){
                    Table.blocks[7]=inv.getItem(11);
                }else{
                    Table.blocks[7]=new ItemStack(Material.AIR);
                }
             
                e.getPlayer().sendMessage("Skin saved");
             
                //saving to file
                //...
     

    Code (Text):

    private void printSingleBlock(int x, int y, int z, int color){
            if(color==7 && transparent){
                Block b=world.getBlockAt(x, y, z);
                for(Player player: whotosendblocksto){
                    SendBlockChangeCustom.sendBlockChangeCustom(player, new Location(world, x, y, z), b);
                }
                return;
            }
           
            for(Player player: whotosendblocksto){
                SendBlockChangeCustom.sendBlockChangeCustom(player, new Location(world, x, y, z), color);
            }
        }
     
    #11 CabbageRoll_, Nov 6, 2020
    Last edited: Nov 6, 2020
  12. I would parse version to a number so you can check like if (Main.version =< 12) { /* do legacy things */ } or even just have a variable isLegacy that you can check instead of using .contains on a string several times.