Solved DisplayName of an arrow.

Discussion in 'Plugin Development' started by Marten Mooij, Aug 2, 2014.

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

    Marten Mooij

    So I want to get an arrow's DisplayName when an Entity is hit (EntityDamageByEntityEvent) but I can't seem to get it to work, I have tried the following:
    Code:java
    1. if(arrow.getMetadata("DisplayName").toString().equalsIgnoreCase(ChatColor.WHITE + main.getConfig().getString("PoisonousArrow-itemName"))){
    2. if(arrow.getMetadata("DisplayName").toString().contains(ChatColor.WHITE + main.getConfig().getString("PoisonousArrow-itemName"))){
    3. if(arrow.getMetadata("DisplayName").toString().equals(ChatColor.WHITE + main.getConfig().getString("PoisonousArrow-itemName"))){ //I wouldn't use this normally

    When I try to see if its valid:
    Code:java
    1. Bukkit.broadcastMessage(arrow.getMetadata("DisplayName").toString());
    It just prints out: [], I have searched this up and someone said that "Arrow entities can't have a DisplayName NBT property. Only mobs can." I am not certain if this is true or not, I have also tried the ProjectileLaunchEvent but that event can't compare the ItemStacks. I already know how to tag the arrow with "Special" MetaData. Any help is appreciate thanks.
     
  2. Offline

    Dragonphase

    Marten Mooij

    getMetadata returns a List of previously set MetadataValues with the same name. Use this to retrieve the first value from that list:

    Code:java
    1. arrow.getMetadata("DisplayName").get(0).asString()
     
    Phazerous likes this.
  3. Offline

    Marten Mooij

    Dragonphase
    So this should return the DisplayName of the arrow? I might be using it wrong but it just gives me an Error.
    Code:java
    1. if(arrow.getMetadata("DisplayName").get(0).asString().contains(ChatColor.WHITE + main.getConfig().getString("PoisonousArrow-itemName"))){
    I also tried broadcasting it:
    Code:java
    1. Bukkit.broadcastMessage(ChatColor.GOLD + arrow.getMetadata("DisplayName").get(0).toString());
    This gives me the same error.
     
  4. Offline

    Dragonphase

    Marten Mooij

    Assuming that you have set the displayname of the arrow previously, using:

    Code:java
    1. (your arrow).setMetadata("DisplayName", new FixedMetadataValue(plugin, "Arrow's name here"));


    This DisplayName won't be visible as a nametag though. What error does it give you?
     
  5. Offline

    Marten Mooij

    Dragonphase
    I have not set the "FixedMetadata", I would just like to know how to check the DisplayName of the arrow when an Entity is hit. I have tried to set the "FixedMetadata" on an EntityShootBowEvent but i can't find a way to check if the fired arrow then has a certain DisplayName.
     
  6. Offline

    Surfdudeboy

    @Marten Mooij I don't think you can check the displayname of an arrow. I don't think it has one/can store one. You can try casting the arrow back into an Entity and then using .getDisplayName(). Never-mind, the Entity class does not have that method in it's interface.

    Someone correct me if I'm wrong, but I believe Bukkit has it's own arbitrary "MetaData" completely separate from what we are familiar with which is Minecraft's "metadata" which is actually NBT tags (I think.) If you edit an entity's "bukkit metadata" It will have no effect on the entity itself without other plugins/code taking that stored arbitrary metadata and using it to do cool things.

    So you should be able to accomplish the same thing using the Metadata API.

    Never used it myself, but here is how I think you use Bukkit's Metadata API. I've copied it from my IDE.
    Code:java
    1. arrow.setMetadata("Marten's arrow data", new FixedMetadataValue(plugin, "Arbitrary arrow information"));
    2. List<MetadataValue> meta = arrow.getMetadata("Marten's arrow data");
     
  7. Offline

    Dragonphase

    Marten Mooij

    Set the metadata in your EntityShootBowEvent and check whether the arrow has metadata in your EntityDamageByEntityEvent:

    Code:java
    1. if (arrow.hasMetadata("DisplayName"))


    For more information on the Metadata API, it's a good idea to visit its Bukkit JavaDocs page.
     
  8. Offline

    fireblast709

    Dragonphase you are not supposed to rely on the List index. Loop over the meta and check if the owning plugin is yours.
     
    Dragonphase likes this.
  9. Offline

    Marten Mooij

    I understand that but then I would first have to check if the shot arrow has a certain DisplayName.
     
  10. Offline

    Dragonphase

  11. Offline

    Surfdudeboy

    Marten Mooij
    I think that is because arrows can't have a displayname/nametag.

    Apply your arbitrary metadata to the arrow on the EntityShootBowEvent Listener using:
    Code:java
    1. arrow.setMetadata("Marten's arrow data key string", new FixedMetadataValue(plugin, "Arbitrary arrow information string"));



    then on the EntityDamageByEntityEvent , check for the metadata with

    Code:java
    1. List<MetadataValue> meta = proj.getMetadata("Marten's arrow data key string");
    2. String stringFromTheArrow = null;
    3. for (MetadataValue value : meta){
    4. if(value.getOwningPlugin().equals(this.plugin)){ //this is just a check to see if this data came from your plugin instead of someone else's.
    5. stringFromTheArrow = value.asString();
    6. }
    7. }
    8. if(stringFromTheArrow != null){
    9. //do things
    10. }
     
  12. Offline

    Marten Mooij

    Surfdudeboy Yes, I understand that but I want it so it doesn't just give all arrows the MetaData only ones with a DisplayName for example "Poisonous Arrow".
     
  13. Offline

    TeeePeee

    I think what you all are missing is that Marten Mooij doesn't want to check if it has some arbitrary meta display name, but rather, he wants to assign it to the name shown on the item. That is, he wants the entity's display name metadata value to match the ItemStack's display name metadata value.

    This poses an interesting problem because when an arrow is fired, there is no simple way to trace it back to the ItemStack the arrow entity originated from.
     
    Surfdudeboy likes this.
  14. Offline

    Marten Mooij

    TeeePeee Thank you for understanding me, I am sorry if I was unclear.
     
  15. Offline

    Surfdudeboy


    Oooooooh. That makes sense now. The bad thing about us being programmers is that we usually can't infer things very well, haha.

    Marten Mooij Right. I'm not 100% sure how to go about it right now, but I can help clear some things up for you. When an arrow ItemStack (with a custom name) is shot from a bow, that name is not carried over normally/by vanilla Minecraft. It simply spawns a plain-jane arrow, no matter what kind of crazy arrow Itemstack was consumed. You will have to somehow find out what arrow ItemStack was shot, take it's Displayname, and then add that information to the Arrow entity using the metadata system we've been talking about before in the thread. That last part is straight forward. Figuring out what ItemStack was consumed is not.
     
  16. Offline

    TeeePeee

    Marten Mooij I'm working on something that might work - I'll edit in my results in ~5 minutes.
     
  17. Offline

    Surfdudeboy

    Cool! This is a pretty interesting problem.
     
  18. Offline

    TeeePeee

    Code:java
    1. @EventHandler
    2. private void onLaunch(final EntityShootBowEvent e) {
    3. // Make this your main class.
    4. final JavaPlugin plugin = this;
    5.  
    6. if (e.getEntity() instanceof Player) {
    7. final Player p = (Player) e.getEntity();
    8. final List<ItemStack> arsenal = new ArrayList<ItemStack>();
    9. for (ItemStack item : p.getInventory().getContents()) {
    10. if (item != null && item.getType() == Material.ARROW) {
    11. // Store all the arrows in the inventory.
    12. arsenal.add(item.clone());
    13. }
    14. }
    15. new BukkitRunnable() {
    16. public void run() {
    17. for (ItemStack item : arsenal) {
    18. if (!p.getInventory().contains(item)) {
    19. // This is the arrow ItemStack that was shot as it
    20. // is no longer there exactly.
    21. String name = item.getItemMeta().hasDisplayName() ? item.getItemMeta().getDisplayName() : "";
    22. System.out.println("A(n) " + name + (name.isEmpty() ? "" : " ") + "arrow has been fired.");
    23. e.getProjectile().setMetadata("ArrowTracker", new FixedMetadataValue(plugin, name));
    24. return;
    25. }
    26. }
    27. }
    28. }.runTaskLater(plugin, 1L);
    29. }
    30. }
    31.  
    32. @EventHandler
    33. private void onHit(EntityDamageByEntityEvent e) {
    34. List<MetadataValue> meta = e.getEntity().getMetadata("ArrowTracker");
    35. String nameMeta = "";
    36. for (MetadataValue value : meta) {
    37. // Where 'this' is your main class.
    38. if (value.getOwningPlugin().equals(this)) {
    39. nameMeta = value.asString();
    40. break;
    41. }
    42. }
    43. if (!nameMeta.isEmpty())
    44. System.out.println("An entity was damaged by a(n) " + nameMeta + " arrow");
    45. }


    Won't work in Creative as no arrows are consumed making it impossible to track. Otherwise it appears to work well :)
     
    Surfdudeboy likes this.
  19. Offline

    Surfdudeboy

    TeeePeee Good work! Try using a zero tick delay for that runnable. I think that will still work. It makes it fire after the event has been finished but before the next tick starts. I like to use a zero tick delay to get the health of an entity after an EntityDamageEvent, instead of its initial health.
     
  20. Offline

    TeeePeee

    Surfdudeboy Yup, zero ticks works too, good catch :) Either way though, 1/20 of a second shouldn't make too much difference.
     
    Surfdudeboy likes this.
  21. Offline

    Surfdudeboy

    TeeePeee not a whole lot, but I can see some scenarios in which it could matter. Point blank shots wouldn't get poison applied. And it may have been possible to use an item dropping macro to trick the plugin into applying poison to normal arrows. I'm a stickler for stuff like that :)

    Oh yeah... Marten Mooij, TeeePeee cleverly solved it above ^ :D
     
    TeeePeee likes this.
  22. Offline

    Marten Mooij

    Code:java
    1. @EventHandler
    2. private void onLaunch(final EntityShootBowEvent e) {
    3. final JavaPlugin plugin = this;
    4.  
    5. if (e.getEntity() instanceof Player) {
    6. final Player p = (Player) e.getEntity();
    7. final List<ItemStack> arsenal = new ArrayList<ItemStack>();
    8. for (ItemStack item : p.getInventory().getContents()) {
    9. if (item != null && item.getType() == Material.ARROW) {
    10. // Store all the arrows in the inventory.
    11. arsenal.add(item.clone());
    12. }
    13. }
    14. new BukkitRunnable(){
    15. public void run(){
    16. for (ItemStack item : arsenal) {
    17. if (!p.getInventory().contains(item)) {
    18. // This is the arrow ItemStack that was shot as it
    19. // is no longer there exactly.
    20. String name = item.getItemMeta().hasDisplayName() ? item.getItemMeta().getDisplayName() : "";
    21. System.out.println("A(n) " + name + (name.isEmpty() ? "" : " ") + "arrow has been fired.");
    22. e.getProjectile().setMetadata("ArrowTracker", new FixedMetadataValue(plugin, name));
    23. return;
    24. }
    25. }
    26. }
    27. }.runTaskLater(plugin, 1L);
    28. }
    29. }
    30. @EventHandler
    31. private void onHit(EntityDamageByEntityEvent e) {
    32. List<MetadataValue> meta = e.getEntity().getMetadata("ArrowTracker");
    33. String nameMeta = "";
    34. for (MetadataValue value : meta) {
    35. // Where 'this' is your main class.
    36. if (value.getOwningPlugin().equals(this)) {
    37. nameMeta = value.asString();
    38. break;
    39. }
    40. }
    41. if (!nameMeta.isEmpty())
    42. System.out.println("An entity was damaged by a(n) " + nameMeta + " arrow");
    43. }
    44. }

    TeeePeee Thank you so much. It doesn't seem to work for me (Im just shooting an arrow at a spider) what am I doing wrong?
     
  23. Offline

    TeeePeee

    Marten Mooij Make sure the class you're using it in is a Listener and is registered for events. Also that you're not in creative mode.

    [EDIT]
    In my haste, I made a stupid mistake...
    Code:java
    1. List<MetadataValue> meta = e.getEntity().getMetadata("ArrowTracker");

    should be
    Code:java
    1. List<MetadataValue> meta = e.getDamager().getMetadata("ArrowTracker");


    [EDIT 2]
    Final code is in a Pastebin here.
     
  24. Offline

    Marten Mooij

    It only goes up to A(n) arrow has been fired for me... I'm not sure what I am doing wrong I registered the events accordingly, Also not in creative mode. :)
     
  25. Offline

    TeeePeee

    Marten Mooij
    See the latest edit. Also, you won't be notified when a normal arrow hits an entity - only when a named arrow hits.

    Also note that Minecraft will use the first arrow in your inventory. Try naming an arrow in an anvil and putting it in the first inventory slot. Then you should be notified properly.
     
  26. Offline

    Marten Mooij

    TeeePeee Thank you the problem was that the arrow wasn't renamed. Thank you everyone in this thead for helping me. :)
     
  27. Offline

    fireblast709

    TeeePeee feed more code, then OP will learn less
     
  28. Offline

    ulsa

    this might be a cool plugin idea , name arrows and give them effects "particles , potion effects , knockbacks .. "
    :)
     
  29. Offline

    TeeePeee

    fireblast709 I'm more of a fan of the learn-through-example method. Exposure to code means familiarity means learning.
     
    Marten Mooij likes this.
  30. Offline

    fireblast709

    TeeePeee by providing code that lacks comments/explanation, and is ready to be copy pasted... You leave no room for him to train his problem solving skills either.

    Moreover your code doesn't even work in all cases due to your arrow shooting tracker using contains. Contains is equals based, which means that if I have two stacks of special arrows, no effects will be applied because the contains will return true (one stack decreases in size. The other still contains 64 arrows, covering both stacks -> no special arrow fired according to the plugin, yet you fired one)
     
Thread Status:
Not open for further replies.

Share This Page