Util [Reflection] Friendly item names

Discussion in 'Resources' started by KingFaris11, Feb 1, 2015.

Thread Status:
Not open for further replies.
  1. I was making a plugin and I required a way to get the friendly names of materials. I searched for a while but all I found was people manually inputting all the names and IDs in a Map, and this was not good as when new items are added, I'll have to manually add them. I then came across @GriffinPvP's comment and what he said to do worked, but I don't like accessing NMS package directly (due to the plugin being version dependent) so I made a Reflection method for getting friendly names.

    Code:
        public static String capitalizeFully(String name) {
            if (name != null) {
                if (name.length() > 1) {
                    if (name.contains("_")) {
                        StringBuilder sbName = new StringBuilder();
                        for (String subName : name.split("_"))
                            sbName.append(subName.substring(0, 1).toUpperCase() + subName.substring(1).toLowerCase()).append(" ");
                        return sbName.toString().substring(0, sbName.length() - 1);
                    } else {
                        return name.substring(0, 1).toUpperCase() + name.substring(1).toLowerCase();
                    }
                } else {
                    return name.toUpperCase();
                }
            } else {
                return "";
            }
        }
    
        public static String getFriendlyName(Material material) {
            return material == null ? "Air" : getFriendlyName(new ItemStack(material), false);
        }
    
        private static Class localeClass = null;
        private static Class craftItemStackClass = null, nmsItemStackClass = null, nmsItemClass = null;
        private static String OBC_PREFIX = Bukkit.getServer().getClass().getPackage().getName();
        private static String NMS_PREFIX = OBC_PREFIX.replace("org.bukkit.craftbukkit", "net.minecraft.server");
    
        public static String getFriendlyName(ItemStack itemStack, boolean checkDisplayName) {
            if (itemStack == null || itemStack.getType() == Material.AIR) return "Air";
            try {
                if (craftItemStackClass == null)
                    craftItemStackClass = Class.forName(OBC_PREFIX + ".inventory.CraftItemStack");
                Method nmsCopyMethod = craftItemStackClass.getMethod("asNMSCopy", ItemStack.class);
    
                if (nmsItemStackClass == null) nmsItemStackClass = Class.forName(NMS_PREFIX + ".ItemStack");
                Object nmsItemStack = nmsCopyMethod.invoke(null, itemStack);
    
                Object itemName = null;
                if (checkDisplayName) {
                    Method getNameMethod = nmsItemStackClass.getMethod("getName");
                    itemName = getNameMethod.invoke(nmsItemStack);
                } else {
                    Method getItemMethod = nmsItemStackClass.getMethod("getItem");
                    Object nmsItem = getItemMethod.invoke(nmsItemStack);
    
                    if (nmsItemClass == null) nmsItemClass = Class.forName(NMS_PREFIX + ".Item");
    
                    Method getNameMethod = nmsItemClass.getMethod("getName");
                    Object localItemName = getNameMethod.invoke(nmsItem);
    
                    if (localeClass == null) localeClass = Class.forName(NMS_PREFIX + ".LocaleI18n");
                    Method getLocaleMethod = localeClass.getMethod("get", String.class);
    
                    Object localeString = localItemName == null ? "" : getLocaleMethod.invoke(null, localItemName);
                    itemName = ("" + getLocaleMethod.invoke(null, localeString.toString() + ".name")).trim();
                }
                return itemName != null ? itemName.toString() : capitalizeFully(itemStack.getType().name().replace("_", " ").toLowerCase());
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            return capitalizeFully(itemStack.getType().name().replace("_", " ").toLowerCase());
        }
    }
    
    Usage:
    Code:
    @EventHandler
    public void onPlayerInteract(PlayerInteractEvent event) {
        if (event.getAction() == Action.RIGHT_CLICK_AIR) {
            if (event.getItem() != null && event.getItem().getType() != Material.AIR) {
                event.getPlayer().sendMessage(ChatColor.RED + "You're holding a(n) " + ChatColor.DARK_RED + getFriendlyName(event.getItem(), true) + ChatColor.RED + "!");
            }
        }
    }
    
    Change 'true' to 'false' if you want it so that it only gets the Miencraft item name, and ignores any display name changes. For example, if a player renames a diamond to "Hi", it won't say "Diamond", it'd say "Hi". If you set 'checkDisplayName' parameter to false, it'll always say "Diamond" and ignore the item name.
     
    Last edited: Jun 17, 2015
    sirrus86 likes this.
  2. Offline

    API_Tutorials

    @KingFaris11
    No need to over complicate things.
    Code:
        public String format(String s)
        {
            if (!s.contains("_"))
            {
                return capitalize(s);
            }
            String[] j = s.split("_");
    
            String c = "";
    
            for (String f : j)
            {
                f = capitalize(f);
                c += c.equalsIgnoreCase("") ? f : " " + f;
            }
            return c;
        }
    
        public String capitalize(String text)
        {
            String firstLetter = text.substring(0, 1).toUpperCase();
            String next = text.substring(1).toLowerCase();
            String capitalized = firstLetter + next;
    
            return capitalized;
        }
    
        public String getFriendlyName(ItemStack item)
        {
    
            Material m = item.getType();
            String name = format(m.name());
    
            return name;
        }
     
    Phasesaber likes this.
  3. Offline

    teej107

  4. Yeah, what you said sums up what I would have said in a lot more detail xD Thanks.

    There is a need to overcomplicate things if you don't want to use the one line code that accesses NMS. Your code is not going to return a "friendly" name which is the whole purpose of this. Your code will show, for example what teej107 said, "THIN_GLASS" as "Thin Glass" instead of "Glass Pane".

    EDIT by Timtower: merged posts
     
    Last edited by a moderator: Feb 1, 2015
  5. Offline

    _Filip

    *tsk* *tsk* *tsk* When I taught you reflection I also told you to cache its all Methods, Classes and Fields. If I catch you doing this again you will be in huge trouble mister.
     
    Phasesaber likes this.
  6. I don't remember you saying that :( xD By that, do you mean make the static and method variables static and only initialise once (when null)? If so, yeah I did that in my plugin unintentionally but didn't do it here.
     
  7. Offline

    GriffinPvP

    Nice :)
     
    KingFaris11 likes this.
  8. Offline

    _Filip

    @KingFaris11 Yeah
    Code:
    private static Method getHandleMethod;
    
    private void doSomething() {
       
       if (getHandleMethod == null) {
           define the value for the getHandleMethod   
    }
    
    do stuff with the method
    
    }
     
  9. Yeah thanks, I did that in my other plugin, except I stored the Class, not the Method, wasn't too sure if I should store the Method incase it could cause something. Edited in this post now.
     
  10. Offline

    RawCode

    items already have "friendly" names localized for each language and defined in lang files.

    if you are going NMS, there is absolutely no reason to avoid core classes and try to implement something via craftbukkit wrappers.
     
  11. Nah, I realised what I said about the localization was rubbish, since the language when using the getName method is the default one for the server itself, it can't change per person

    Also, what do you mean by that? Are you saying that using reflection for the craftbukkit wrappers is fine, but when you use it for NMS classes, there's no point? Orrr what... The only reason I avoid NMS is because of the version dependancy though... I know that method names change but that can be updated easily.
     
  12. Offline

    RawCode

    it can't change per person but you allowed to change "locale" of server before fetching name for user and then changing it back.

    i not going to explain obvious things.
     
  13. I'll explain why you're being stupid in your last sentence, with both definitions of "obvious".

    Firstly, the definition of "obvious" that I use mostly is "predictable and lacking in subtlety." according to Google.
    This shows the thing you're trying to explain is predictable and the majority should be able to know it by making sense of it, but in fact, the majority of people making Bukkit plugins probably don't even know about Reflection and barely about NMS. It's not very obvious to me.

    Secondly, the other definition of "obvious" according to Google is "easily perceived or understood; clear, self-evident, or apparent."
    It's clearly not easily understood or apparent as I do not understand what you mean, hence why I was asking, though you, from what I infer (which could be wrong) try and seem like you're the best and you know it all which you also seem to do in your other comments on other posts, and think that if you know it and it's easy for you, people who don't know it will still find it easy to understand.

    Finally, if you're trying to criticize code, at least be helpful and suggest solutions or explain, otherwise, your comments are irrelevant and don't even comment. Not only that, but you could at least put some effort into using proper grammar, punctuation and spelling.
     
    Phasesaber likes this.
  14. Offline

    RawCode

    nothing to say?
    end of any arguments?
    GROMMER NOZI MODE ENGADE!

    final friendly tip - this is development forum, not forum for users who barely able to handle java for dummies.

    i wont waste my time in this thread anymore, expect valid version of "friendly item names" snippet today.
     
  15. "nothing to say" - clearly why there's many words above, it seems you're pretty illiterate.

    Oh haha, I'm sorry, I'm the stupid one here, I didn't realise that beginners at Java, aka. "Dummies", learnt reflection, because you know, reflection's like so easy to understand when you first program.

    What? Next you'll tell me that hacking governments is "Hacking for dummies".

    Anyway, this is off-topic, so I'm ending this here.
     
    Last edited: Feb 6, 2015
  16. Yeah, but the problem with this is that every update, I believe all the obfuscated code names change, thus a() may be different in another update.

    Also, I'm using Reflection here.
     
  17. Reflection to bypass Bukkit's safeguard is a bad idea.
     
  18. Doesn't that imply reflection is ALWAYS a bad idea (to access NMS)?

    I don't understand why it's bad here, since I'm using real class names and method names, not obfuscated ones like a() b() etc.
     
  19. @KingFaris11 Reflection is not always a bad, nor is it always bad to access NMS classes that way. Using it to bypass the safeguard is. Maybe it's unlikely that the methods are going to change, but it's an implementation, there are no guarantees. They can easily change with no warning. Even if the methods you're calling never change, it's still bad practice and lazy to teach this sort of practice.
     
    KingFaris11 likes this.
  20. Offline

    Abstract97

    Couldn't you just use
    Code:
    CraftItemStack.asNMSCopy(yourItemStack).getName();
     
  21. That is half of exactly what I've done, the other half is checking for display names. But as I said, this uses Reflection, not accessing NMS directly. However, according to Adam, this should be avoided.
     
  22. Offline

    atesin

    yes i think so either .... but i think also is not so probable because it is the first method in the class

    and if it changes is easy to guess the new name and change it, i think even with reflection it cant "auto rename" methods, so we have no option
     
  23. It doesn't have to be to do with method names, though that's one significant bit of it (e.g. a()), it is also to do with the Bukkit version and having to change it every-time there's a new update, also meaning that it is not backwards compatible.

    But yes whatever, people don't have to use the Reflection method of this, just do the same thing as I did but using NMS/OBC directly instead of using reflection.
     
  24. Offline

    _Filip

    Well you can probably calculate the method you need to use by going through all the methods in the thing that return a string, then check the methods against a itemstack you already know the name of, say stone then remember the method that when invoked returns "Stone"
     
  25. Still requires reflection though, so what's the point? Or is the reason for this the fact reflection isn't required to access NMS because reflection itself is fine but as Adam said, bypassing the safeguard is not fine.
     
  26. Offline

    _Filip

    What are you saying?? This would let you safely bypass the "version safeguard" and future proof it
     
  27. Oh I just re-read Adam's statement which said:
    So nvm, yeah you're right xD Before, I was thinking it was stupid because it doesn't matter about the safeguard, the method name for getName() most likely wouldn't change, but I guess adam's statement makes sense.
     
  28. Offline

    guitargun

    Not to have some troubles but this method is also a nice one to use with nms.
    Code:
        public String GetItemName(ItemStack item){
            return CraftItemStack.asNMSCopy(item).getName();
        }
    
    I only tested it on glass it works with all those Id types
     
  29.  
  30. Offline

    guitargun

    KingFaris11 likes this.
Thread Status:
Not open for further replies.

Share This Page