Solved NPE help; Object type help

Discussion in 'Spigot Plugin Development' started by LukeEff, Jan 27, 2020.

  1. Hi, been working on this project for a week or so and had to rebuild some class files for compatibility with a config file realtime reload.
    I had it working, but changed a bunch of things in that class and the other class and I can't seem to figure out why.
    Here's the error:
    Code (Text):
    Caused by: java.lang.NullPointerException
        at io.github.lukeeff.ShieldListener.getShieldProgress(ShieldListener.java:229) ~[?:?]
        at io.github.lukeeff.ShieldListener.shieldProcessing(ShieldListener.java:137) ~[?:?]
        at io.github.lukeeff.ShieldListener.playerDamaged(ShieldListener.java:124) ~[?:?]
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_221]
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_221]
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_221]
        at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_221]
        at org.bukkit.plugin.java.JavaPluginLoader$1.execute(JavaPluginLoader.java:316) ~[spigot-1.15.1.jar:git-Spigot-037559e-661bebc]

    Here's my class file generating the exception:
    Code (Java):
    package io.github.lukeeff;

    import java.util.HashMap;
    import java.util.UUID;
    import java.util.concurrent.TimeUnit;

    import org.bukkit.Bukkit;
    import org.bukkit.ChatColor;
    import org.bukkit.boss.BarColor;
    import org.bukkit.boss.BarStyle;
    import org.bukkit.boss.BossBar;
    import org.bukkit.boss.KeyedBossBar;
    import org.bukkit.entity.Player;
    import org.bukkit.event.EventHandler;
    import org.bukkit.event.Listener;
    import org.bukkit.event.entity.EntityDamageEvent;
    import org.bukkit.event.player.PlayerJoinEvent;
    import org.bukkit.scheduler.BukkitRunnable;

    public class ShieldListener implements Listener {

        static eShields plugin;
        static ShieldCooldown shieldCooldown;
        static ShieldSounds shieldSounds;
        //static BossBar playerShield;
        static public BarColor fracturedShieldColor; // Color when shield health hits 0.
        static public BarColor healthyShieldColor; // Color when shield begins to regenerate.
        static public BarStyle shieldStyle; // Style for shield.
        static public double shieldRegenPerTick; // Regenerate shield %/tick.
        static public String shieldName; // Name above BossBar that player sees.
        static public double shieldHealth; // Gives the shield a value of 1/2 heart per health.
        @SuppressWarnings("rawtypes")
        static
        HashMap<UUID, HashMap> data; // Hashmap holding each player's BossBar.

        @SuppressWarnings("rawtypes")
        public ShieldListener(eShields instance) {
            plugin = instance;
            setVariables();
        }
       
        static void setVariables() {
            shieldSounds = plugin.sounds;
            fracturedShieldColor = configSectionGetBarColor(plugin.fracturedShieldColor);
            healthyShieldColor = configSectionGetBarColor(plugin.healthyShieldColor);
            shieldCooldown = plugin.cooldown;
            shieldStyle = configSectionGetBarStyle(plugin.shieldStyle);
            shieldRegenPerTick = (configSectionGetDouble(plugin.shieldRegenPerTick) / 2000);
            shieldName = configSectionGetString(plugin.shieldName);
            shieldHealth = configSectionGetDouble(plugin.shieldHealth);
            data = plugin.data;
        }
       
        static void reload() {
            setVariables();
            for (Player player : Bukkit.getOnlinePlayers()) {
                UUID uuid = player.getUniqueId();
                BossBar playerShield = (BossBar) data.get(uuid).get("playerShield");
                updateShield(playerShield);
            }
           
        }

        private static void updateShield(BossBar playerShield) {
            if(playerShield.getProgress() <= 0.01) {
                playerShield.setColor(fracturedShieldColor);
            } else {
                playerShield.setColor(healthyShieldColor);
            }
            playerShield.setStyle(shieldStyle);
        }
        // TODO Fix very rare bug where damage doesn't trigger health regen for some
        // reason.
        // TODO Give a way to spectate battles and see shield status of any player.
        // TODO Keep functionality on reload.
        // TODO Put long comments over line not to side.
        // TODO Declare in outter wrapper, assign them in construcuter.
        // TODO Brainstorm ideas for player-exclusive options.

        /*  
         * Defines player object and BossBar for map key and value pair. BossBar is what
         * I used for the energy shield.
         */

       
       
       
        @EventHandler
        private void onJoin(PlayerJoinEvent event) {
            Player player = (Player) event.getPlayer();
            BossBar playerShield = Bukkit.createBossBar(shieldName, healthyShieldColor, shieldStyle);
            storeData(player, playerShield);
            initializeShield(player, playerShield);

        }

       
       
        /*
         * This method is for future configuration that I haven't added yet.
         */

        private void storeData(Player player, BossBar shield) {
            HashMap<String, Object> contents = new HashMap<String, Object>();
            contents.put("playerShield", shield);
            data.put(player.getUniqueId(), contents);
        }

        /*
         * Initializes the shield and makes it visible to our player.
         */

        private void initializeShield(Player player, BossBar shield) {
            shield.addPlayer(player);
            shield.setVisible(true);
        }

        /*
         * Defines player object and the damage we will add to the shield.
         */

        @EventHandler
        private void playerDamaged(EntityDamageEvent event) {
            if (event.getEntity() instanceof Player) {
                double shieldDamage = (event.getDamage() / 100); // Divide by 100 so player takes miniscule damage. Can't
                                                                    // set to 0.
                Player player = (Player) event.getEntity();
                shieldProcessing(player, shieldDamage, event);

            }
        }

        /*
         * Assigns a shield regen cooldown for the player. Checks if the shield health
         * is greater than 0.
         */

        private void shieldProcessing(Player player, double shieldDamage, EntityDamageEvent event) {
            BossBar playerShield = getPlayerShield(player);
            setShieldCooldown(player);
            beginShieldRestoreTimer(player, playerShield);
            if (getShieldProgress(playerShield) > 0d) {
                try {
                    setShieldProgress(playerShield, getShieldProgress(playerShield) - shieldDamage * (shieldHealth / 4)); // Divide    20 5 5
                                                                                                                            // by
                                                                                                                            // 4
                                                                                                                            // so
                                                                                                                            // shieldHealth
                                                                                                                            // works
                                                                                                                            // correctly.
                    event.setDamage(shieldDamage); // Sets player damage to 1% if shield is active.
                } catch (Exception negativeShieldHealth) {
                    shieldFracture(player, shieldDamage, playerShield, event);
                }
            }
        }

        /*
         * Store cooldown in HashMap.
         */

        private void setShieldCooldown(Player player) {
            shieldCooldown.setCooldown(player.getUniqueId(), System.currentTimeMillis());
        }

        /*
         * Changes player's BossBar color. Sets player damage to leftover damage of
         * shield.
         */

        private void shieldFracture(Player player, double shieldDamage, BossBar playerShield, EntityDamageEvent event) {
            playerShield.setColor(fracturedShieldColor);
            event.setDamage(((event.getDamage() - (getShieldProgress(playerShield) * shieldHealth))));
            setShieldProgress(playerShield, 0);
            playShieldFractureSound(player);
        }

        /*
         * Restore shield in DEFAULT_COOLDOWN seconds.
         */

        private void beginShieldRestoreTimer(Player player, BossBar playerShield) {
            Bukkit.getScheduler().runTaskLater(plugin, () -> restoreShield(player, playerShield),
                    toTicks(io.github.lukeeff.ShieldCooldown.DEFAULT_COOLDOWN) + 1); // Add one tick or canRegen will always
                                                                                        // say false.
        }

        /*
         * Begins shield restoration if player is allowed to regen.
         */

        private void restoreShield(Player player, BossBar playerShield) {

            long timeLeft = getTimeLeft(player);
            if (canRegen(timeLeft)) {
                playShieldRegenSound(player);
                beginShieldRestore(player, playerShield);
            }
        }

        /*
         * Repeat task until shield is full or setShieldCooldown is called. Task
         * restores shield at shieldRegenPerTick per tick. Allows for a smooth and
         * cancelable shield regen.
         */

        private void beginShieldRestore(Player player, BossBar playerShield) {
            playerShield.setColor(healthyShieldColor);
            new BukkitRunnable() {

                @Override
                public void run() {

                    long timeLeft = getTimeLeft(player);

                    if (!canRegen(timeLeft)) {
                        this.cancel();
                    } else if (getShieldProgress(playerShield) + shieldRegenPerTick >= 1) {
                        setShieldProgress(playerShield, 1);
                        this.cancel();
                    } else {
                        setShieldProgress(playerShield, getShieldProgress(playerShield) + shieldRegenPerTick);
                    }
                }
            }.runTaskTimer(plugin, 0L, 1L);
        }

        /*
         * Checks if cooldown has expired.
         */

        private boolean canRegen(long timeLeft) {
            return TimeUnit.MILLISECONDS.toSeconds(timeLeft) >= io.github.lukeeff.ShieldCooldown.DEFAULT_COOLDOWN;
        }

        /*
         * Returns player's BossBar progress.
         */

        private double getShieldProgress(BossBar playerShield) {
            return playerShield.getProgress(); //Throws NPE...
        }

        /*
         * Sets player's BossBar progress
         */

        private void setShieldProgress(BossBar playerShield, double progress) {
            playerShield.setProgress(progress);
        }

        /*
         * Plays sound for shield fracturing.
         */

        private void playShieldFractureSound(Player player) {
            shieldSounds.shieldFracture(player);
        }

        /*
         * Plays sound for shield regeneration.
         */

        private void playShieldRegenSound(Player player) {
            shieldSounds.shieldRegen(player);
        }

        /*
         * Returns player's BossBar
         */

        private BossBar getPlayerShield(Player player) {
            return (BossBar) data.get(player.getUniqueId()).get("shieldMap");
        }

        /*
         * Returns the time left.
         */

        private long getTimeLeft(Player player) {
            return System.currentTimeMillis() - shieldCooldown.getCooldown(player.getUniqueId());
        }

        /*
         * Converts seconds to ticks and returns.
         */

        private long toTicks(long seconds) {
            return seconds * 20;
        }

        /*
         * Returns configuration section name for objects.
         */

        private static String configSectionName() {
            return plugin.getShieldListenerSectionName();
        }

        /*
         * Returns config string value from shieldListenerSection specified in
         * parameter.
         */

        private static String configSectionGetString(Object[] configName) {
            return plugin.getConfig().getConfigurationSection(configSectionName()).getString((String) configName[0]);
        }

        /*
         * Returns config double value from shieldListenerSection specified in
         * parameter.
         */

        private static double configSectionGetDouble(Object[] configName) {
            return plugin.getConfig().getConfigurationSection(configSectionName()).getDouble((String) configName[0]);
        }

        /*
         * Returns config BarStyle value from shieldListenerSection specified in
         * parameter.
         */

        private static BarStyle configSectionGetBarStyle(Object[] configName) {
            return plugin.shieldStyleMap.get(plugin.getConfig().getConfigurationSection(configSectionName())
                    .getString((String) configName[0]).toUpperCase());
        }

        /*
         * Returns config BarColor value from shieldListenerSection specified in
         * parameter.
         */

        private static BarColor configSectionGetBarColor(Object[] configName) {
            // return
            // plugin.getConfig().getConfigurationSection(configSectionName()).getObject((String)
            // configName[0],
            // BarColor.class);
            return plugin.shieldColorMap.get(plugin.getConfig().getConfigurationSection(configSectionName())
                    .getString((String) configName[0]).toUpperCase());
        }

    }


    My next question was with a method I have in another class. I'm trying to check if objects inside of a config file are part of a whitelist I have stored in a HashMap. I convert them to strings to compare them, but I want to allow any string type for string objects in my config, double for double, etc, but I do this all in a single method block with variables, limiting me to using objects. What I have mostly works, but I want to know if there's a way I can get the type of object that is in the HashMap valueMap that I'm passing in. I tried a lot, but couldn't get it to work.

    Code (Java):
        private boolean checkIfValid(HashMap<String, ?> valueMap, String section, String configKey) {
            for (Object value : valueMap.values().toArray()) {

                String mapString = value.toString();
                String configString = getSectionObject(section, configKey).toString();

                if (mapString.equals(configString)) {

                    return true;
                } else if (getSectionObject(section, configKey) instanceof Double
                        | getSectionObject(section, configKey) instanceof Integer
                        | (getSectionObject(section, configKey) instanceof String) & valueMap.equals(stringMap)) {

                    return true;
                }
            }
            return false;
        }
     
    • Optimistic Optimistic x 1
  2. Replace the hash so with a custom class.
     
    • Useful Useful x 1
  3. Update:
    Fixed the bug. Apparently I changed the name of the key on my HashMap that was supposed to return my playershield LOL.


    I'll give this a shot and get back to you when it's working, fantastic idea! Thanks :)

    Going to change this to solved