Solved Dependency Injection Issue

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

  1. Still new to Spigot programming and dependency injections which is leading to some logic errors.

    Context: My plugin comes with a default config.yml that writes itself into the plugin folder iff the config.yml file doesn't already exist. I have edited the default config for personal use on a localhost server. My main class can access the edited config fine, but in a different class, I noticed that my null handling was getting triggered while trying to access sections of the config. After some testing, I found that it is accessing the default config.yml that I have edited, and is no longer present in my plugin folder.

    I'm pretty confident that this is a product of my inability to write dependency injections very well. I've pasted in the code that is called.

    Main class:
    Code (Text):
    package com.abstractionalpha.minecraft.plugins.chestrandomizer;

    import org.bukkit.ChatColor;
    import org.bukkit.command.Command;
    import org.bukkit.command.CommandSender;
    import org.bukkit.plugin.Plugin;
    import org.bukkit.plugin.java.JavaPlugin;
    import java.util.ArrayList;

    /**
    * Main class containing executables. Handles command processing and user interface.
    *
    * @version 1.3
    * @author abstractionAlpha
    */
    public final class ChestRandomizer extends JavaPlugin {

        IdTools id = new IdTools();

        FillChests fillChests = new FillChests(this.getConfig());

        EmptyChests emptyChests = new EmptyChests(this.getConfig());

        private static Plugin plugin;

        /**
         * Plugin start-up logic.
         */
        @Override
        public void onEnable() {
            plugin = this;
            saveDefaultConfig();
        }

        /**
         * Method allowing plugin to be accessed by other classes.
         *
         * @return plugin
         */
        public static Plugin getPlugin() {
            return plugin;
        }

        /**
         * Plugin shut-down logic.
         */
        @Override
        public void onDisable() {}

        /**
         * Handles command inputs from user.
         *
         * @param cmd Command issued (looks for '/cr')
         * @param sender Player or console who issued command
         * @param args Command-line arguments
         * @return Boolean representing successful or unsuccessful execution of command
         */
        @Override
        public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
            if (cmd.getName().equalsIgnoreCase("cr")) {
                if (args.length > 2) {
                    sender.sendMessage(ChatColor.RED + "Too many arguments. Type '/cr' for help.");
                    return true;
                } else if (args.length == 0) {
                    sender.sendMessage(ChatColor.RED + "/cr <fill|empty> <region|radius|all>");
                    return true;
                }

                String methodType;
                if (args[0].equalsIgnoreCase("fill")) {
                    methodType = "fillType";
                } else if (args[0].equalsIgnoreCase("empty")) {
                    methodType = "emptyType";
                } else {
                    sender.sendMessage(ChatColor.RED + "Invalid case!");
                    return true;
                }

                String methodArgument;
                if (args[1].equalsIgnoreCase("all")) {
                    methodArgument = "allArg";
                } else if (id.isNumeric(args[1])) {
                    methodArgument = "radiusArg";
                } else {
                    methodArgument = "regionArg";
                }

                ArrayList<String> chestList = (ArrayList<String>) getConfig().getStringList("chests.chest-list");
                String chestToFill;
                String chestToEmpty;
                switch (methodArgument) {

                    case "allArg":
                        if (methodType.equalsIgnoreCase("fillType")) {
                            for (int i=0; i < chestList.size(); i++) {
                                chestToFill = chestList.get(i);
                                fillChests.fill(chestToFill);
                            }
                            sender.sendMessage(ChatColor.GOLD + "[ChestRandomizer] Chests filled!");
                        } else {
                            for (int i=0; i < chestList.size(); i++) {
                                chestToEmpty = chestList.get(i);
                                emptyChests.empty(chestToEmpty);
                            }
                            sender.sendMessage(ChatColor.GOLD + "[ChestRandomizer] Chests emptied!");
                        }
                        break;

                    case "radiusArg":
                        int rad = Integer.valueOf(args[1]);
                        if (methodType.equalsIgnoreCase("fillType")) {
                            for (int i=0; i < chestList.size(); i++) {
                                chestToFill = chestList.get(i);
                                fillChests.fill(chestToFill, rad, sender);
                            }
                            sender.sendMessage(ChatColor.GOLD + "[ChestRandomizer] Chests in radius " + args[1] + " filled!");
                        } else {
                            for (int i=0; i < chestList.size(); i++) {
                                chestToEmpty = chestList.get(i);
                                emptyChests.empty(chestToEmpty, rad, sender);
                            }
                            sender.sendMessage(ChatColor.GOLD + "[ChestRandomizer] Chests in radius " + args[1] + " emptied!");
                        }
                        break;

                    case "regionArg":
                        if (methodType.equalsIgnoreCase("fillType")) {
                            for (int i=0; i < chestList.size(); i++) {
                                chestToFill = chestList.get(i);
                                fillChests.fill(chestToFill, args[1]);
                            }
                            sender.sendMessage(ChatColor.GOLD + "[ChestRandomizer] Chests in region " + args[1] + " filled!");
                        } else {
                            for (int i=0; i < chestList.size(); i++) {
                                chestToEmpty = chestList.get(i);
                                emptyChests.empty(chestToEmpty, args[1]);
                            }
                            sender.sendMessage(ChatColor.GOLD + "[ChestRandomizer] Chests in region " + args[1] + " emptied!");
                        }
                        break;

                    default:
                        sender.sendMessage(ChatColor.RED + "null");
                        return true;
                }


            } return true;
        }
    }
     
    Class triggering null handling:
    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.3
    * @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) {
            chestPath = config.getConfigurationSection("chests." + chestName);
            if (chestPath == null) {
                ChestRandomizer.getPlugin().getLogger().info("No configuration section found at chests." + chestName);
                return;
            }

            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) {
            chestPath = config.getConfigurationSection("chests." + chestName);
            if (chestPath == null) {
                ChestRandomizer.getPlugin().getLogger().info("No configuration section found at chests." + chestName);
                return;
            }

            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) {
            chestPath = config.getConfigurationSection("chests." + chestName);
            if (chestPath == null) {
                ChestRandomizer.getPlugin().getLogger().info("No configuration section found at chests." + chestName);
                return;
            }

            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 + ".list");
            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)));
                }
            }
        }
    }
     
    Any help?
     
    • Funny Funny x 1
  2. Well right off the bat I notice you are making everything static which inherently has it's own problems. But more importantly you seem to not grasp the fundamental concepts of object oriented design. You are creating an instance of fill chests where you pass in a config as a parameter then you try to do this.config = config. But there is no this.config as it does not belong to the instance "this" but to the class itself because you have made it static. In this context the value of config is shared between all instances of the class, yet you are creating an instance of the class to try and set its value. You then proceed to use the instance you created in main to access static methods within FillChests which is also incorrect. Start by removing all your static modifiers
     
    • Useful Useful x 1
  3. Thanks for the reply. You're totally right - I'm relatively new to Java (1st semester uni freshman) and still figuring things out. I've determined that the main problem here is a result of my dependency injection, and my flaws there are a result of bad readings on advice from more experienced programmers than me in another post.

    I've updated my code and posted it in the link above in order to try and isolate the key issue. Could you refer to that thread? I'd appreciate some specific pointers there. As well, if you are willing, I'd appreciate if you PM me on Spigot or Discord to talk about static abuse. (Problem I'd like to resolve but not sure where to start.)
     
  4. Closed, 95% of issues resulting from the fact that I can't spell