Solved Help with some Debugging and logic.

Discussion in 'Spigot Plugin Development' started by Sirenum, May 15, 2016.

  1. So I've come across a bug that I can't squash.

    What I have is a game class.

    • There is an ArcherKit class that sets the attributes of that class to the player, then adds that user's UUID to an ArrayList<UUID>.
    • Lastly, I have an ArcherListener class that listens for the a certain amount of distance between the two players and if they are far enough away from each other, and one user shoots the other, severe damage will occur to the victim. This works fine. The issue I have encountered is that I don't want any player to be able to headshot anyone without using the archer class. (The commands can be restricted to certain Game worlds only)

    Here I am on a test server with my friend. I have issued the command that sets the archer attributes to myself, and I had my friend do the same. Then I executed a debug command to make sure that our UUID's were both added.
    [​IMG]

    Here is where I attempted to test the event. I shoot my friend within the correct distance, and my error message is returned. My problem is, that both users are contained in the arraylist and so the event should be working.
    [​IMG]

    All console activity during the test.
    [​IMG]


    Code (Text):
    public class ArcherKit extends KitUtils
    {

        ArrayList<UUID> isArcher = new ArrayList<>();
       
        public ArrayList<UUID> isUserArcher()
        {
            return isArcher;
        }
       
    //Attribute method.
    }

    Code (Text):
    if(kit.isUserArcher().contains(victim.getUniqueId()) && kit.isUserArcher().contains(shooter.getUniqueId()))
    {
        //Do stuff if True... It should be.
    }else
    {
                //My error message.
                shooter.sendMessage(color("%prefix% &7Target must be using the archer kit!"));
    }
     
     
  2. Do you have multiple instances of the kit? e. g. you registered commands for 1 but events for 2
     
  3. Run a check on both the shooter and victim. Log this before the if statement
    Code (Text):
    kit.isUserArcher().contains(victim/shooter.getUniqueId())
    You might be getting the UUID of the Entity that hit the victim, in this case - the Arrow, not the UUID of the shooter.
     
  4. Here is how I get an instance of the kit command.

    Code (Text):
    public class ArcherEvent extends KitUtils implements Listener
    {

    //    Core main;

        ArcherKit kit;

        public ArcherEvent(ArcherKit instance)
        {
            kit = instance;
        }
       
       @EventHandler
        public void onEntityDamageByEntity(EntityDamageByEntityEvent event)
        {

           if(kit.isUserArcher().contains(victim.getUniqueId()) && kit.isUserArcher().contains(shooter.getUniqueId()))
            {
                   //do stuff      
            }else
            {
                shooter.sendMessage(color("%prefix% &7Target must be using the archer kit!"));
            }

        }
     
    }
     
    These two methods handle all command and kit events.
    Both are run inside the onEnable()# method.
    Code (Text):
    void kitCommands()
        {
           registerCmd("class", new CoreKitCommand(this));
        }

        void registerGameEvents()
        {
            Debug.log(Debug.pluginLog()  + " Registering game events...");
            PluginManager pm = Bukkit.getServer().getPluginManager();

            ArcherKit kit = new ArcherKit();
            Debug.log(Debug.pluginLog() + Debug.ACTION + "Archer kit constructor");

            pm.registerEvents(new ArcherEvent(kit),this);
        }
     
     
  5. I think you might be right. I will check back in a second.
     
  6. How do you add the players to the Kit? I see you have a CoreKitCommand, but how can that add a player to the kit in the ArcherEvent?
     
  7. I do this inside the ArcherKit class. I have a set method just like this:

    //ArcherKit.java



    Code (Text):


    ArrayList<UUID> isArcher = new ArrayList<>();

    public ArrayList<UUID> isUserArcher()
    {
        return isArcher;
    }


    public void setArcher(Core main_class, Player p)
    {
    //The parameter main_class is just so I can access some configuration options, and serves no other purpose other than that.
       if(!isArcher.contains(uuid))
       {

                    isArcher.add(uuid);
                    inventory.clear();
                    clearArmor(p);
                    Debug.log(p,getGamePrefix() + "Equipped the user, " + name + " UUID: " + uuid.toString() + "with the Archer kit!");
                    Debug.log(getGamePrefix() + "Equipped the user, " + name + " UUID: " + uuid.toString() + "with the Archer kit!");
          //So on and so forth... Adding all items and equipment to the player inventory.
       }
    }
    The set method is called here, inside the CoreKitCommand class:

    Code (Text):
    switch (args[0].toLowerCase())
                {
                           case "archer":
                                if(!p.hasPermission(KitPermissions.CLASS_COMMAND_ARCHER))
                                {
                                    p.sendMessage(color(main.getGameSettings().getGameConfig().getString("Settings.cmd-perm-msg")));
                                }else
                                {
                                    Debug.log(getGamePrefix() +" Command Class " + " Archerkit call.");
                                    archerKit.setArcher(main,p);
                                }
                                break;
              }
     
  8. You can put metadatas into entities including arrows.
    use EntityShotBowEvent to listen bow shots(be sure shooter is player)
    (I didn't put the player and archer check to the code)
    Then mark the projectile with;
    Code (Text):

        @EventHandler
        public void entd(EntityShootBowEvent e){
            if(e.getEntity() instanceof LivingEntity){
                ItemStack is = e.getBow();
                if(is == null){return;}
                if(is.getType().equals(Material.AIR)){return;}
                e.getProjectile().setMetadata("archer_kit", new FixedMetadataValue(Main.getPlugin(), e.getForce()));
            }
        }
     
    Main.getPlugin() is a method i use in my plugins,use your JavaPlugin object instead of that.
    This code simply puts a metadata named "archer_kit" and set it's value to arrow's shoot speed.

    In EntityDamageByEntityEvent check that attacker is an arrow,and if it is check that arrow has the metadata we put in the EntityShotBowEvent.
    Code (Text):
     @EventHandler
         public void endm(EntityDamageByEntityEvent e){
             if(e.isCancelled()){return;}
             Entity attacker = e.getDamager();
             if(!(e.getEntity() instanceof LivingEntity)){return;}
             LivingEntity victim = (LivingEntity) e.getEntity();
            //If attacker is a projectile and not a living entity.
             if(attacker instanceof Projectile && !(attacker instanceof LivingEntity)){
                 //Cast the attacker to projectile.
                 Projectile pr = (Projectile) attacker;
                 //Check the metadata we put earlier in EntityShootBowEvent
                 if(pr.hasMetadata("archer_kit")){
                     //Metadatas are multi map,means that they got one key and multiple values.
                     //We put only one value to 'archer_kit' so get(0) will do the trick
                     Float projspeed = pr.getMetadata("archer_kit").get(0).asFloat();
                     //Do stuff,this attack is definetly dealt by a player with archer kit
                 }
             }
         }
     
    • Agree Agree x 1
  9. Interesting concept. Never thought of it like that. Any reason why I would take this approach as compared to what I am already trying to do?
     
  10. Because it's a really good approach to the problem, a bit out-of-the-box. This would work with entities other than players. And learning how to use Metadata is really useful :p
     
    • Like Like x 1
  11. Well believe it or not, I'm almost positive this was partly the problem.

    [​IMG]

    As you can see by the console output, it is clear that it isn't picking up any UUID's that were added to the isArcher arrayList.

    here is that section of the code:

    Code (Text):
      // Victim
            LivingEntity victim = (LivingEntity) event.getEntity();
            Arrow arrow = (Arrow) event.getDamager();

             //Shooter
             Player shooter = (Player) arrow.getShooter();



            Debug.log(Debug.pluginLog() + " " +getGamePrefix() +Debug.ACTION+ "&cVictim.  Name: &6" +victim.getName() + " &cUUID: &6" + victim.getUniqueId());

            Debug.log(Debug.pluginLog() +" " +getGamePrefix() + Debug.ACTION + "&cShooter.  Name: &6" +shooter.getName() + " &cUUID: &6" + shooter.getUniqueId());

            Debug.log(Debug.pluginLog() + " " + getGamePrefix() + "&cCurrent UUID's: &6" + kit.isUserArcher());
     
    It is picking up both UUID's, however, when I try to access this variable, it would seem the UUID's are missing.
    How is this possible?
     
  12. Can you share the whole CommandExecutor class? I pretty sure that the ArcherKit you're adding the UUIDs to is not the same with the one you're checking the UUIDs against.
     
  13. @FineasGavre
    Code (Text):
    package KitAPI;

    import KitAPI.Kits.ArcherKit;
    import Utilities.CoreHelpMenus;

    import Utilities.Debug;
    import me.ES.Core.Core;
    import org.bukkit.ChatColor;
    import org.bukkit.command.Command;
    import org.bukkit.command.CommandExecutor;
    import org.bukkit.command.CommandSender;
    import org.bukkit.entity.Player;

    import java.util.List;

    /**
    * Created by ES359 on 5/11/16.
    */
    public class CoreKitCommand extends KitUtils implements CommandExecutor
    {

        Core main;
        ArcherKit archerKit = new ArcherKit();

        public CoreKitCommand(Core instance)
        {
            main = instance;
        }

        private boolean commandEnabled()
        {
            return main.getGameSettings().getGameConfig().getBoolean("Settings.command-blocked.Enabled");
        }

        CoreHelpMenus menu = new CoreHelpMenus();
        public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String args[])
        {

            List<String> worlds = main.getGameSettings().getGameConfig().getStringList("Settings.command-blocked.Enabled-Worlds");

            if(!(sender instanceof Player))
            {

                sender.sendMessage(ChatColor.RED + "Sorry, the console cannot use kits.");
                return true;
            }

            Player p = (Player)sender;

            if(commandEnabled() && !(worlds.contains(p.getWorld().getName())))
            {
                p.sendMessage(color(main.getGameSettings().getGameConfig().getString("Settings.command-blocked.no-perms-msg")));
            }else
            {
                if(cmd.getName().equalsIgnoreCase("class"))
                {
                    if(!p.hasPermission(KitPermissions.CLASS_COMMAND))
                    {
                        p.sendMessage(color(main.getGameSettings().getGameConfig().getString("Settings.cmd-perm-msg")));
                    }else if(args.length < 1)
                    {
                        sendText(menu.classMenu(),p);
                    }else if(args.length > 0)
                    {
                        switch (args[0].toLowerCase())
                        {
                            case "archer":
                                if(!p.hasPermission(KitPermissions.CLASS_COMMAND_ARCHER))
                                {
                                    p.sendMessage(color(main.getGameSettings().getGameConfig().getString("Settings.cmd-perm-msg")));
                                }else
                                {
                                    Debug.log(getGamePrefix() +" Command Class " + " Archerkit call.");
                                    archerKit.setArcher(main,p);
                                }
                                break;
                            case "amt":
                                p.sendMessage(color("%prefix% &a" + archerKit.isUserArcher()));

                            default:
                                p.sendMessage(color(main.getGameSettings().getGameConfig().getString("Settings.arguments-error")));
                        }
                    }
                }
            }
            return true;
        }
    }
     
     
  14. As I thought, your ArcherKit from the CoreCommandKit.class is a different one from the one in the Event.
    Code (Text):
    //CORECOMMANDKIT
    ArcherKit archerKit = new ArcherKit();
    In both your CoreCommandKit and Main classes you're making a new ArcherKit, each of them being separate. You can quickly fix the issue by making only one ArcherKit somewhere both classes can access. (mainly Main class)
    Code (Text):
    // MAIN
    public ArcherKit archerKit = new ArcherKit();
    Or you can instanciate ArcherKit in the ArcherEvent class, and make that available from the main class, and access that from CoreCommandKit.
     
    • Winner Winner x 1
  15. And there you have it!

    [​IMG]

    You can see in the Current UUID's debug, that it is showing both players, and the damage code executes!

    Thanks to all of you!

    @FineasGavre
    @Zindev (I plan to try out your method as well)
    @megamichiel