How to get matarial from items.yml?

Discussion in 'Plugin Development' started by makcoh2018, Jun 26, 2021.

Thread Status:
Not open for further replies.
  1. Offline

    makcoh2018

    Hey, i try to make a command for upgrade items. I have a problem. I need to get material from my items.yml for example
    Material nextItemMaterial = Material.getMaterial(main.instance.itemsConfig.(HOW I CAN COMPLITE THIS?)

    My items.yml
    Code:
    axe_1:
      price: 100
      material: wooden_axe
      name: '&aWOODEN_AXE'
      requirements:
        blocks:
          LOG: 10
      lore:
        '&6LEVEL 1'
      next: axe_2
    
    axe_2:
        price: 300
        material: wooden_axe
        name: '&aWOODEN_AXE'
        requirements:
          blocks:
            LOG: 50
        enchantments:
          DIG_SPEED: 2
        lore:
          '&6lEVEL 2'
        next: axe_3
    
    axe_3:
      price: 500
      material: wooden_axe
      name: '&aWOODEN_AXE'
      requirements:
        blocks:
          LOG: 100
      enchantments:
        DIG_SPEED: 2
      lore:
        '&6LEVEL 3'
     
  2. Offline

    Strahan

    Are you not understanding how to get the Material object correctly, or how to get the next material? I assume the latter as you seem to have a handle on the former. If so, you need to do some String parsing. This assumes all your items follow the same format of (type)_(level).

    Code:
    public Map<String, Object> nextItem(String currentItem) {
      Split the currentItem on _ and return null if the length isn't 2
      Make a nextLevel int from adding 1 to the split'd array's element 1
      Be sure to trap NumberFormatException.  If it throws, log an error and return null
    
      Make a Map<String, Object> to hold returned data
      Make a ConfigurationSection by grabbing split'd array[0] + "_" + nextLevel
      If it's null, add a "maxlevel" key to the map and return
    
      Loop the configuration section, adding the data to the map. 
      For the material, create a Material object from the data and add that.
      If there are errors in the config data, log it and return null
    }
    
    Some function where you need this {
      Map<String, Object> nextItem = nextItem(current item's code);
      If nextItem is null, tell the player there is a server issue and abort
      If nextItem containsKey maxlevel, tell them they already have the best version of this item
    
      Create item from the data you retrieved.  When you do it, cast appropriately:
      ItemStack nextItemObject = new ItemStack((Material)nextItem.get("material"));
      ItemMeta im = nextItemObject.getItemMeta();
      im.setDisplayName(ChatColor.transblahblah(nextItem.get("name").toString()));
    }
    I wrote this off the top of my head, it isn't tested but should be a general idea for it. You can just do the map as String,String too and make Material when needed but IMO I prefer to use Object and cast so it simplifies the usage part. I prefer to do any "heavy lifting" in my sourcing functions.

    Also you could just return material alone if that's all you need, but I just figured I'd illustrate how I'd personally do it.
     
  3. Offline

    makcoh2018

    Thanks for you answer. I can't solve my problem. I need to check if there is an item from hand in items.yml and get id. I the waste of 10 hours on solving problem but my knowledge don't enough. I will be very glad for your help.


    My code:
    Code:
    public class Upgrade implements CommandExecutor, Listener {
        String id = null;
        private Inventory inv;
    
        public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
            Player p = (Player)sender;
            if (command.getName().equalsIgnoreCase("upgrade")) {
                if (p.getItemInHand().getType().equals(Material.AIR)) {
                    p.sendMessage(ChatColor.RED + "Take tool in you hand!");
                    return true;
                    }
                if (p.getItemInHand().getItemMeta().getLore() == null) {
                    p.sendMessage(ChatColor.RED + "This tool can't be upgrade!");
                    return true;
                    }
                List<String> lore = p.getItemInHand().getItemMeta().getLore();
                Iterator<String> iterator = lore.iterator();
                String s = iterator.next(); 
                if (!iterator.hasNext() ||s.contains((CharSequence)ChatColor.BLACK)) {
    
                    for (String key : Main.instance.itemsConfig.getKeys(false)) {
                        if (!lore.contains(ChatColor.BLACK + key)) { // I DON'T KNOW WHAT THIS STRING TO DO (MAYBE THIS PROBLEM STRING)
                            this.id = null;
                            continue;
                            }
                        this.id = key;
                        break;
                        }
                    if (this.id == null) {
                        p.sendMessage(ChatColor.RED + "This tool can't be upgrade!"); //EVERYTHING I SEE THIS STRING IN THE GAME
                        return true;
                        }
                    if (!Main.instance.itemsConfig.contains(String.valueOf(this.id) + ".next")) {
                        p.sendMessage(ChatColor.GOLD + "This tool upgrade for max");
                        return true;
                        }
    My wooden_axe have lore name '&aWOODEN_AXE' and lore add '&6LEVEL 1'
     
  4. Offline

    Strahan

    Well, there is a lot wonky in there but the main problem is the only time you set id is if the lore contains &0 and the key from the config. The keys for itemsConfig.getKeys(false), assuming it uses the config you first posted, are:

    axe_1
    axe_2
    axe_3

    So if you have axe_1 you have a wooden axe with name &aWOODEN_AXE and a lore of &6LEVEL 1. So when it checks that in the loop, the lore will never contain it so it will end up null. Also the this keyword is only for when there is a conflict between a method and a class scope variable. The id variable is not conflicting in the method, so this. is superfluous. It also should not be class scope in the first place.

    Other things I noticed:
    Code:
    String id = null;
    As mentioned before, this really should not be here but in the method instead

    Code:
    private Inventory inv;
    This isn't used

    Code:
    public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
    It's not required, but you really should get in the habit of using @Override annotations on overriden methods

    Code:
    Player p = (Player)sender;
    sender isn't always a Player, so you should check before casting

    Code:
    if (command.getName().equalsIgnoreCase("upgrade")) {
    As the name of the class is Upgrade, it makes me believe this only handles the upgrade command. In that case, this is not necessary to check as it can only be the upgrade command

    Code:
    if (p.getItemInHand().getType().equals(Material.AIR)) {
    Are you using 1.8 or something? If you are not, then that is deprecated and all similar calls should be replaced with getInventory().getItemInMainHand()

    Code:
    p.sendMessage(ChatColor.RED + "Take tool in you hand!");
    I assume English isn't your first language. That should say something like "Place the tool to upgrade in your hand first". Not saying that to be a dick, I respect anyone who can be conversant in multiple languages. Just figured you'd want input from a native speaker :)

    Code:
    p.sendMessage(ChatColor.RED + "This tool can't be upgrade!");
    "This tool can't be upgraded!"

    Code:
    List<String> lore = p.getItemInHand().getItemMeta().getLore();
    getItemMeta() is nullable, and there is no guarantee the item will have ItemMeta so you should check that first else it will crash if they use an item w/o meta.

    Code:
    Iterator<String> iterator = lore.iterator();
    This isn't really necessary. An iterator is only really needed when you want to modify what you are looping. Otherwise, an "enhanced for loop" would suffice.

    Code:
    String s = iterator.next();
    if (!iterator.hasNext() ||s.contains((CharSequence)ChatColor.BLACK)) {
    That's not how to do that. The first line is calling for next, then the NEXT line is checking if it even has any data next. You should be using a while loop instead.

    Code:
    p.sendMessage(ChatColor.RED + "This tool can't be upgrade!"); //EVERYTHING I SEE THIS STRING IN THE GAME
    "This tool can't be upgraded!"

    Code:
    p.sendMessage(ChatColor.GOLD + "This tool upgrade for max");
    "This tool is already at max upgrade level!"

    I don't like spoon feeding, but if you want to see how I'd do this with actual code using your config, this is how. I just made this and it tests fine for me in game.
    Show Spoiler
    Code:
    @Override
    public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
      if (!(sender instanceof Player)) {
        sender.sendMessage("Not for use by console");
        return true;
      }
    
      Player p = (Player)sender;
      if (cmd.getName().equalsIgnoreCase("cmd1")) {
        ItemStack i = new ItemStack(Material.WOODEN_AXE);
        ItemMeta im = i.getItemMeta();
        im.setDisplayName(ChatColor.translateAlternateColorCodes('&', "&aWOODEN_AXE"));
        im.setLore(Arrays.asList(ChatColor.translateAlternateColorCodes('&', "&6LEVEL 1")));
        i.setItemMeta(im);
        p.getInventory().addItem(i);
        return true;
      }
    
      if (cmd.getName().equalsIgnoreCase("cmd2")) {
        ItemStack i = p.getInventory().getItemInMainHand();
        if (i.getType() == Material.AIR) {
          p.sendMessage("Place the tool to be upgraded in your hand first!");
          return true;
        }
      
        String id = getItemID(i), nextID = "";
        if (id.equals("")) {
          p.sendMessage("This tool cannot be upgraded!");
          return true;
        }
      
        if ((nextID = getConfig().getString(id + ".next")) == null) {
          p.sendMessage("This tool is not upgradable!");
          return true;
        }
      
        double remaining = chargeUpgrade(p, nextID);
        if (remaining < 0) {
          getLogger().info("** Someone attempted to upgrade " + id + " to " + nextID + " but the cost could not be determined!");
          p.sendMessage("There was a problem processing your upgrade, please see an admin.");
          return true;
        } else if (remaining > 0) {
          p.sendMessage("You don't have enough money to upgrade!  You need " + remaining + " yet.");
          return true;
        }
      
        ItemStack newItem = getNextItem(nextID);
        p.getInventory().setItemInMainHand(newItem);
      }
      return true;
    }
    
    private String getItemID(ItemStack i) {
      if (!i.hasItemMeta() || !i.getItemMeta().hasLore()) return "";
    
      ItemMeta im = i.getItemMeta();
      for (String key : getConfig().getKeys(false)) {
        Material m = Material.matchMaterial(getConfig().getString(key + ".material", "BARRIER"));
        if (m == null || m != i.getType()) continue;
    
        for (String lore : im.getLore()) {
          if (!lore.equals(ChatColor.translateAlternateColorCodes('&', getConfig().getString(key + ".lore", "")))) continue;
    
          return key;
        }
      }
      return "";
    }
    
    private ItemStack getNextItem(String id) {
      ConfigurationSection cfg = getConfig().getConfigurationSection(id);
      if (cfg == null) return null;
    
      Material m = Material.matchMaterial(cfg.getString("material"));
      if (m == null) return null;
    
      ItemStack i = new ItemStack(m);
      ItemMeta im = i.getItemMeta();
      String displayName = ChatColor.translateAlternateColorCodes('&', cfg.getString("name", ""));
      String lore = ChatColor.translateAlternateColorCodes('&', cfg.getString("lore", ""));
      if (!displayName.equals("")) im.setDisplayName(displayName);
      if (!lore.equals("")) im.setLore(Arrays.asList(lore));
      i.setItemMeta(im);
      return i;
    }
    
    private double chargeUpgrade(Player p, String id) {
      ConfigurationSection cfg = getConfig().getConfigurationSection(id);
      if (cfg == null) return -1;
    
      try {
        double cost = Integer.parseInt(String.valueOf(getConfig().getString(id + ".price")));
        getLogger().info("[[ " + eco.getBalance(p));
        if (eco.getBalance(p) < cost) return cost - eco.getBalance(p);
    
        eco.withdrawPlayer(p, cost);
        return 0;
      } catch (NumberFormatException ex) {
        return -1;
      }
    }

    It's not exactly as you are doing; I was adding it to my testbed plugin so my commands are /cmd1 to give myself a level 1 item with which to test upgrading and /cmd2 to do the upgrade. It also assumes you have an initialized economy system. But then, this isn't meant to be lifted and used just to show you one way to approach the issue.
     
    Last edited: Jun 28, 2021
    Shqep likes this.
Thread Status:
Not open for further replies.

Share This Page