Solved Remove hard-coding in method led to IllegalArgumentException

Discussion in 'Spigot Plugin Help' started by abstractionAlpha, Sep 9, 2020.

  1. Hey, all:

    I'm working on a plugin that fills chests with random items based on their different levels or tiers. I wanted to change it so that, instead of working with one tier "random" for random tiers, any tier containing the word random would function the same way.

    Upon trying to make this change (simply exchanging the hard-coded random for the name of a designated configuration section), it now returns a IllegalArgumentException saying the tierprobs.get(rand.nextInt(tierprobs.size()))'s bound must be positive. Worked fine before.

    Any help? I'll post code in the main thread and relevant links as a comment below for approval purposes. Let me know if more info is needed.

    FillChests method (see public static String randomTier(String randTier))
    Code (Text):
    package com.abstractionalpha.minecraft.plugins.chestrandomizer;

    import org.bukkit.Bukkit;
    import org.bukkit.Location;
    import org.bukkit.Material;
    import org.bukkit.World;
    import org.bukkit.block.Block;
    import org.bukkit.block.Chest;
    import org.bukkit.command.CommandSender;
    import org.bukkit.configuration.ConfigurationSection;
    import org.bukkit.configuration.file.FileConfiguration;
    import org.bukkit.entity.Player;
    import org.bukkit.inventory.ItemStack;

    import java.util.ArrayList;
    import java.util.Random;

    /**
    * Class containing functionality for filling chests. 3 main methods depending on method argument and two for function.
    *
    * @version 1.2
    * @author abstractionAlpha
    */
    public class FillChests  {

        private static FileConfiguration config;

        public FillChests(FileConfiguration config){
            this.config = config;
        }

        private static ConfigurationSection chestPath;
        private static Integer x;
        private static Integer y;
        private static Integer z;
        private static String tier;

        /**
         * Method that finds chest to fill.
         *
         * @param chestName Name of chest being filled.
         */
        public static void fill(String chestName) {
            try {
                chestPath = config.getConfigurationSection("chests." + chestName);
            } catch (Exception e) {
                e.printStackTrace();
            }

            String chest = chestPath.getCurrentPath();

            x = config.getInt(chest + ".x");
            y = config.getInt(chest + ".y");
            z = config.getInt(chest + ".z");

            String world;
            Boolean manualWorld = config.isSet(chest + ".world");
            if (manualWorld) {
                world = config.getString(chest + ".world");
            } else {
                world = config.getString("default-world");
            }

            tier = config.getString(chest + ".tier");

            World w = Bukkit.getWorld(world);
            Block b = w.getBlockAt(x, y, z);

            if(b != null && b.getType() == Material.CHEST) {
                Chest c = (Chest) b.getState();
                c.getInventory().clear();

                if (tier.toLowerCase().contains("random")) {
                    tier = randomTier(tier);
                }

                doFill(c, tier);

            } else {
                Bukkit.getLogger().info("Chest " + chestName + " at " + x + " / " + y + " / " + z + " in world " + world + " not found.");
            }
        }

        /**
         * Method that finds chest to fill within specified radius.
         *
         * @param chestName Name of chest being filled.
         * @param radius Radius to filter chests by.
         */
        public static void fill(String chestName, Integer radius, CommandSender sender) {
            try {
                chestPath = config.getConfigurationSection("chests." + chestName);
            } catch (Exception e) {
                e.printStackTrace();
            }

            String chest = chestPath.getCurrentPath();

            x = config.getInt(chest + ".x");
            y = config.getInt(chest + ".y");
            z = config.getInt(chest + ".z");

            String world;
            Boolean manualWorld = config.isSet(chest + ".world");
            if (manualWorld) {
                world = config.getString(chest + ".world");
            } else {
                world = config.getString("default-world");
            }

            int radiusSquared = radius * radius;
            double distSquared;

            if (sender instanceof Player) {
                Player player = (Player) sender;
                Location loc = player.getLocation();
                distSquared = (x-loc.getX()) * (x-loc.getX()) + (z-loc.getZ()) * (z-loc.getZ());
            } else {
                distSquared = x * x + z * z;
            }


            if (distSquared <= radiusSquared) {

                tier = config.getString(chest + ".tier");

                World w = Bukkit.getWorld(world);
                Block b = w.getBlockAt(x, y, z);

                if(b != null && b.getType() == Material.CHEST) {
                    Chest c = (Chest) b.getState();
                    c.getInventory().clear();

                    if (tier.toLowerCase().contains("random")) {
                        tier = randomTier(tier);
                    }

                    doFill(c, tier);

                } else {
                    Bukkit.getLogger().info("Chest " + chestName + " at " + x + " / " + y + " / " + z + " in world " + world + " not found.");
                }

            }

        }

        /**
         * Method that finds chest to fill within specified region.
         *
         * @param chestName Name of chest being filled.
         * @param region Region to filter chests by.
         */
        public static void fill(String chestName, String region) {
            try {
                chestPath = config.getConfigurationSection("chests." + chestName);
            } catch (Exception e) {
                e.printStackTrace();
            }

            String chest = chestPath.getCurrentPath();

            x = config.getInt(chest + ".x");
            y = config.getInt(chest + ".y");
            z = config.getInt(chest + ".z");

            String world;
            Boolean manualWorld = config.isSet(chest + ".world");
            if (manualWorld) {
                world = config.getString(chest + ".world");
            } else {
                world = config.getString("default-world");
            }

            String regionFound = config.getString(chest + ".region");

            if (regionFound.equals(region)) {

                tier = config.getString(chest + ".tier");

                World w = Bukkit.getWorld(world);
                Block b = w.getBlockAt(x, y, z);

                if(b != null && b.getType() == Material.CHEST) {
                    Chest c = (Chest) b.getState();
                    c.getInventory().clear();

                    if (tier.toLowerCase().contains("random")) {
                        tier = randomTier(tier);
                    }

                    doFill(c, tier);

                } else {
                    Bukkit.getLogger().info("Chest " + chestName + " at " + x + " / " + y + " / " + z + " in world " + world + " not found.");
                }

            }
        }

        /**
         * Method that generates a random tier from the "random" list in config.
         *
         * @param randTier Name of random tier inputted by a fill command
         * @return Randomly generated tier
         */
        public static String randomTier(String randTier) {
            ArrayList<String> randtiers = (ArrayList<String>) config.getStringList("tiers." + randTier);
            ArrayList<String> tierprobs = new ArrayList<>();
            for (int j = 0; j < randtiers.size(); j++) {
                for (int k = 0; k < config.getInt("tiers." + randTier + "." + randtiers.get(j) + ".percent"); k++) {
                    tierprobs.add(randtiers.get(j));
                }
            }
            Random rand = new Random();
            return tierprobs.get(rand.nextInt(tierprobs.size()));
        }

        /**
         * Method that puts items in chests.
         *
         * @param c Chest found by fill()
         * @param tier Tier of chest c
         */
        public static void doFill(Chest c, String tier) {

            IdTools id = new IdTools();

            ArrayList<String> tieritems = (ArrayList<String>) config.getStringList("tiers." + tier + ".items");
            Integer min = config.getInt("tiers." + tier + ".min-items");
            Integer max = config.getInt("tiers." + tier + ".max-items") + 1;
            ArrayList<Integer> itemcounts = new ArrayList<Integer>();
            for (int j = min; j < max; j++) {
                itemcounts.add(j);
            }
            Integer itemcount = (int) id.getRandomElement(itemcounts);
            for (int j = 0; j < itemcount; j++) {
                Random rand = new Random();
                String item = tieritems.get(rand.nextInt(tieritems.size()));
                Integer count = rand.nextInt(5);
                if ((item.contains("SWORD") == true) || (item.contains("AXE") == true) || (item.contains("BOW") == true) || (item.contains("SHIELD") == true)) {
                    count = 1;
                } else if (((item.contains("HELMET") == true) || (item.contains("CHESTPLATE") == true) || (item.contains("LEGGINGS") == true) || (item.contains("BOOTS") == true))) {
                    count = 1;
                }
                for (int amountOfItems = 0; amountOfItems < count; amountOfItems++) {
                    c.getBlockInventory().addItem(new ItemStack(Material.valueOf(item)));
                }
            }
        }
    }
     
    config.yml
    Code (Text):
    default-world: world
    chests:
      chest-list:
        - chest-1
        - chest-2
        - chest-3
        - chest-4
        - chest-5
      chest-1:
        x: 371
        y: 63
        z: 202
        tier: common
        region: arena-1
      chest-2:
        x: 163
        y: 20
        z: 237
        tier: rare
        region: arena-1
      chest-3:
        x: 320
        y: 23
        z: 716
        tier: very-rare
        region: arena-1
        world: world_nether
      chest-4:
        x: 1075
        y: 44
        z: 1516
        tier: random
      chest-5:
        x: 1077
        y: 44
        z: 1516
        tier: random-2
    tiers:
      common:
        min-items: 3
        max-items: 6
        items:
          - WOODEN_SWORD
          - STONE_SWORD
          - WOODEN_AXE
          - LEATHER_HELMET
          - LEATHER_CHESTPLATE
          - LEATHER_LEGGINGS
          - LEATHER_BOOTS
          - GOLDEN_HELMET
          - GOLDEN_CHESTPLATE
          - GOLDEN_LEGGINGS
          - GOLDEN_BOOTS
          - APPLE
          - MELON
          - BEEF
      uncommon:
        min-items: 2
        max-items: 4
        items:
          - STONE_SWORD
          - IRON_SWORD
          - STONE_AXE
          - IRON_HELMET
          - IRON_CHESTPLATE
          - IRON_LEGGINGS
          - IRON_BOOTS
          - CHAINMAIL_HELMET
          - CHAINMAIL_CHESTPLATE
          - CHAINMAIL_LEGGINGS
          - CHAINMAIL_BOOTS
          - BAKED_POTATO
          - BREAD
          - CARROT
          - COOKED_RABBIT
          - COOKED_CHICKEN
      rare:
        min-items: 1
        max-items: 3
        items:
          - IRON_SWORD
          - IRON_AXE
          - SHIELD
          - DIAMOND_HELMET
          - DIAMOND_BOOTS
          - DIAMOND_SWORD
          - COOKED_MUTTON
          - COOKED_RABBIT
          - COOKED_CHICKEN
          - COOKED_PORKCHOP
          - COOKED_SALMON
          - COOKED_BEEF
      very-rare:
        min-items: 1
        max-items: 2
        items:
          - DIAMOND_SWORD
          - SHIELD
          - NETHERITE_HELMET
          - DIAMOND_CHESTPLATE
          - DIAMOND_LEGGINGS
          - GOLDEN_APPLE
          - GOLDEN_CARROT
      random:
        list:
          - common
          - uncommon
        common:
          percent: 33
        uncommon:
          percent: 67
      random-2:
        list:
          - rare
          - very-rare
        rare:
          percent: 33
        very-rare:
          percent: 67
     
  2. What is the size of the collection when it tries to call Random#nextInt? How do you know the collection isn't empty (returning 0 in that case)?
     
    • Agree Agree x 1
  3. Supposing the sample configuration section tiers.random, tierprobs should be adding one common for each iteration, where the number of iterations is the value of tiers.random.common.percent. Upon writing that out, I realize that randtiers wasn't accessing tiers.random.list... so, thanks for making me think about it!