Basic developing tips for better coded plugins

Discussion in 'Resources' started by Milkywayz, Jul 5, 2012.

?

Which technique saves most performance?

  1. Correct use of Logging

    8.3%
  2. Correct use of the final keyword

    8.3%
  3. Correct use of Collections

    66.7%
  4. Correct use of the contains() method

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

    Milkywayz

    When I look through the plugin development forum I often come across code that is either inefficient, soon to be deprecated, or doesn't make sense. This is also true when looking at the source code of released plugins. Eitherway theres a few things that should be noted. If you disagree with any of the following by all means present your case, most of us are here to learn ^^.
    1. Logging:
    When you want to log messages to console theres a few options you have, probably the two most common are:
    PHP:
    public static final Logger log Logger.getLogger("Minecraft");
    log.info("message");
    System.out.println("message");
    The preferred method of logging would be to use:
    PHP:
    plugin.getLogger();
    or
    public static 
    Logger log Bukkit.getLogger();
    log.info*("message");
    That way you can access the logger throughout your entire plugin.
    Also the very first method allows for your messages to be prefixed with your plugin name, but this method of logging may be replaced soon.

    2. The final keyword:
    It can be argued that the final keyword increases performance because the final keyword means that the class, object, or variable cannot be changed after initialization. Therefore the JVM can perform better, this is subject to debate but its better to not use the final keyword unless your using anonymous inner classes. The keyword final is however useful for.. stuff you don't want to be changed programatically? :p For example:
    Code:
    public abstract interface Version {
     
      public static final String CURRENT = "1.0";
    }

    3. Collections:
    Java collections is a very awesome part of java. You have the option to store data in a list where its sorted, in a hash map where theres a key and a value, in a hash set where there can be no duplicates, or in a array. When you have a lot of strings that are similar, for example you name all the potions in strings, instead of declaring each string individually you can declare an array using:
    Code:
    String[] foods = {"melonslice", "zombieflesh", "rawpork", "steak", "chicken"};
    Arrays are cool because you can turn that array into a list, where it is ordered and then iterate through that list using an advanced for loop:
    Code:
    for(String food : Arrays.asList(foods)) {
    if(food.equals("steak") {
    Bukkit.getLogger().log("STEAK!");
    } else {
    Bukkit.getLogger().log("aww no steak");
    continue;
    }
    }
    Your output would be:
    aww no steak
    aww no steak
    aww no steak
    STEAK!
    aww no steak
    Collections are a great way to store your data, an a suitable compromise from using databases. Although the two can be combined using queues in java (or just array lists) to allow you to buffer data flow into the database.

    4. Do you need a for loop?
    When your working with events, you usually don't need to use a for loop to iterate through a list (or other collection). You can simply check if the list contains the object your looking for, for example if we want to get the world during an event we can use:
    Code:
    // You have a list of worlds in config
    // Lets simulate that in the code
    String[] worldz = {"world", "world_nether", "world_end");
    List<String> worlds = Arrays.asList(worldz);
     
    @EventHandler
    public void onEvent(PlayerInteractEvent e) {
    if(worlds.contains(e.getPlayer().getWorld().getName()){
      // do stuff
    }
    }
    Rather than:
    Code:
    String[] worldz = {"world", "world_nether", "world_end");
    List<String> worlds = Arrays.asList(worldz);
     
    @EventHandler
    public void onEvent(PlayerInteractEvent e) {
    for(String w : worlds)) {
    if(w.equalsIgnoreCase(e.getPlayer().getWorld().getName())) {
    // do stuff
    }
    continue;
    }
    }
    The later causes a check to happen for as many worlds you have, although its usually not many, the additional code may cause performance issues on events called often.
    5. Don't try to trick the compiler
    I don't have a good way to outline this, but when you code try to code in a way that makes sense to you but also take into account that making overly complex code that in theory should safe memory usually does little difference and ends up leaving you with sloppy code.
    The java runtime environment does do some optimizations on its own, and it does to garbage collection. That does not mean you shouldn't set heavy objects to null when done using them :p
    Have anything to contribute here? Post is as a reply and I will add it! ^^
     
    HON95 and Icyene like this.
  2. Offline

    nisovin

    1. The first method is probably better, since the plugin logger will prefix the log entries with your plugin name.

    4. The object backing your List is going to run that loop and all those checks when you use contains(), so the reason to use it isn't really performance, but cleaner, prettier code. If you want to improve performance on contains checks, you should use something like a HashSet.
     
  3. Offline

    desht

  4. Offline

    Milkywayz

    True, but the title of the thread is "basic developing tips for better coded plugins" :D Also when i created this thread I didn't an example for a decent hash set and i do now.

    Yes i've seen that article before and that's where I got the idea that final was good but since then I had many trusted sources refute that. I did say it was up for debate :p

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: May 26, 2016
  5. Offline

    desht

    What trusted sources are those? For object fields in particular, correct use of final is a Very Good Thing.
     
  6. Offline

    Milkywayz

    Some of my fellow bukkitdev staff members. Since this thread is about learning, I can see where final would be a good thing and I still do use it in my code. But the performance difference would only be noticed if we had a lot of object fields :p
     
  7. Offline

    desht

    There's a lot more than just performance to the use of final. Immutable objects, proper aggregation of objects... both contribute greatly to more robust code.
     
  8. Offline

    Milkywayz

    Agreed, the keyword final is not used very often in my opinion, and usually when it used it's by people who copy and paste code. ;)
     
  9. Offline

    md_5

    Don't use static fields on non-primitives as they will leak on /reload.
    When storing data the preference should likely be Array->Set->List, think about how you will be using that data!


    Those are my two top tips.
     
  10. Offline

    Milkywayz

    Nice tips, thanks for sharing!
     
  11. Offline

    Sagacious_Zed Bukkit Docs

    You can use a for loop without even turing an array into a list explicitly.

    Code:
    for (String s : new String[] {"apple", "bob", "charllie"}) {
        getLogger().fine(s);
    }
    Also instead of turning the array into a list and calling contains. you can sort the array and run a binary search against it to see if it is there. But may be more expensive if you have to continuously sort an array.
     
  12. Offline

    Digi

    But if you have static pointers to your main class and other classes to use in static getters... setting them to null inside onDisable() would prevent that leakage ?
    Also, even tough they're re-asigned in onEnable() they'll still cause problems ?
    example (open)
    Code:
    	protected static RecipeManager	plugin;
    	protected static Settings		settings;
    	protected static Recipes		recipes;
    	protected static Econ			economy;
    	protected static Permissions	permissions;
    	protected static Events			events;
    	private static Metrics			metrics;
    
    	public void onEnable()
    	{
    		plugin = this;
    		settings = new Settings();
    		recipes = new Recipes();
    		economy = new Econ();
    		permissions = new Permissions();
    		events = new Events();
    	}
    
    	public void onDisable()
    	{
    		plugin = null;
    		settings = null;
    		recipes = null;
    		economy = null;
    		permissions = null;
    		events = null;
    	}

    EDIT: I mean, even if I don't set them as null in onDisable, why will they cause a leak when the reference is no longer used since a newer one is asigned ? That is, if the plugin reloads and it's not only disabled, for that it's the null setting in onDisable.

    Or people that use Eclipse's cleaner which I belive adds final wherever possible by default.

    EDIT: also, for the vote, you should allow multiple options :p I would vote for 3 and 4 because mis-using those would yeld alot of bad performance, expecially together.
     
  13. Offline

    md_5

    :confused: I would open Eclipse just to do that.
     
  14. Offline

    Digi

    md_5
    You still didn't answer my question :p
     
  15. Offline

    md_5

    Yeah, but what about people that use say static configs, ie Config.mayValue, that could be a lot to null.
     
  16. Offline

    Digi

    Huh ?
    I meant this:
     
  17. Offline

    md_5

    Yes it will work, but could be a lot of effort.
    Thats what I said.
     
  18. Offline

    Digi

    Ok good to know, I've already done it so just wanted to know if it's good or not :}
     
Thread Status:
Not open for further replies.

Share This Page