Build 1317 new config

Discussion in 'Plugin Development' started by Perdog, Oct 10, 2011.

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

    Kalman Olah

    getList(String path) is still returning null for valid paths for me...
    The actual snippet:
    Code:
        @SuppressWarnings("unchecked")
        private static List<String> readStringList(String root) {
            YamlConfiguration config = load();
            return (List<String>)config.getList(root);
        }
     
  2. Offline

    bergerkiller

    And I just converted .txt based configuration to .yml node-based configuration in MyWorlds...this is not going to be good. :/

    After reading this, I kinda join the side of @tehbeard I already wrote 'SafeReader' and 'SafeWriter' classes before to simplify my text file loading and saving needs. I jumped to YAML, simply, because it doesn't require me to:
    - Handle exceptions, just check if the default is returned (null)
    - Getting the configuration assured me that the config.yml exists or is made
    - No annoying 'does this file exist' lines during every config file I load
    - Not needed to store a file path along the way

    Now this is lost (as I read from here) I doubt if I will use it again...or I end up writing a wrapper around the configuration, which seems pretty weird to do for a Configuration/Properties-based system...
     
  3. Offline

    Dinnerbone Bukkit Team Member

    Fixed in a dev build.

    What exactly do you need from this? Is that bullet point your list of issues? If so:
    - Don't need this. Where are you seeing this?
    - Certainly wasn't true for the last one. All you have to do is call saveConfig() for this one.
    - Don't need this.
    - Is it really so hard to keep track of a small variable?
     
  4. Offline

    bergerkiller

    @Dinnerbone in that case I am assured. I noticed load and save was throwing exceptions, so that's why I mentioned the first point. And the fourth one, nope it's not hard, but it does litter the code now and then. And it's kinda weird to have 'FileConfiguration' without the actual file path inside...what distincts it from the regular Configuration?

    EDIT

    And good point about the intlist...are all lists 'String lists' for now on, and do we need to handle parsing them ourselves, or will the getList return a List<Integer> in those cases?
     
  5. Offline

    Dinnerbone Bukkit Team Member

    It's file based, but that doesn't mean it saves directly to file. It takes:
    - Stream
    - Writer/Reader
    - File
    - String

    This means it can download a config, or use an embedded file config, or make one up from scratch.


    In regards to load/save:
    If you're using the standard config.yml, you have getConfig() reloadConfig() and saveConfig() methods provided for you. If you're not, then it's a simple:
    Code:
    FileConfiguration config = YamlConfiguration.loadConfiguration(somefile);
    
    // Optionally, if you have some defaults in your plugin:
    config.setDefaults(YamlConfiguration.loadConfiguration(getResource("path/to/default/config.yml"));
    
    // Optionally, if you want to make sure those defaults are copied to the output of the users config if any of them aren't set by the user:
    config.options().copyDefaults(true);
    
    Save still has an exception to catch, but I'm sure you can deal with that.
     
  6. Offline

    bergerkiller

    @Dinnerbone Ah ok, so it's more of a Stream wrapper now, instead of a FileStream wrapper. :)

    I'll look into this tomorrow then, will find out how everything works. Only afraid I get lots of people complaining that the plugin fails on cb#1240...
     
  7. Offline

    Farlin

    Ok, after much fighting on this one, I finally figured it out. Its good, it just takes some effort to understand the logic. Mind you not being able to reference a dot notation path deeper than 2 levels to retrieve the keys is annoying (I see a fix is in the latest dev build). I did work around this, its messy, but works.

    Say you have a config key of "my.option.is.deep" and you want to get all the sub-keys of that key (sub-keys could be option1, anotheroption, etc). I had to do something like below for the time being:

    ConfigurationSection configOptions = this.plugin.getConfig().getConfigurationSection("my.option").getConfigurationSection("is.deep");

    for(String key : keys.getKeys(false))
    {
    // spit out the key or do something fun -- these will be relative keys and not absolute (i.e. option1, anotheroption)
    }

    Note you should be checking for nulls with configOptions and handling accordingly.

    I did not use the Bukkit addDefault API, I prefer to iterate through the configuration values on startup and validate them against the expected options list and define the defaults so they are forcefully set in the config if not already defined. That's just me though.

    Great improvement in my mind, just will take some getting used to at first.

    EDIT: I would like to see or know of a way to add comments to lines in the config or at least a header through the new API.
     
  8. @Farlin I think I saw the header thing somewhere around (YamlConfiguration).options().setHeader("bla")
     
  9. Offline

    Perdog

    It only uses one string though, you cant split the header up
     
  10. Offline

    desht

    @Dinnerbone

    So. The new API is actually pretty good now, for basic config reading and writing needs. It's a change, but I'll agree the new API is better than the old one.

    However, for reading/writing persisted data...

    Yes, the new API is better, if you're starting a new project. I like the persistence functionality that's been added, it's elegant.

    But if you're trying to make an existing project compatible with the new API... not so nice. The big problem is that it's no longer possible to do config.set(key, map) where map is some kind of Map. This worked in the old API, and did what you'd expect - serialise the map into YAML that made sense in the persisted data file. I used (arguably abused) that by adding to my persisted classes a freeze() method which turned the object into a Map (and a corresponding method to construct/thaw an object from a Map). Then I could freeze any object, and it in turn could freeze any aggregated objects, and the whole lot would get written to a YAML file, which could contain one or more levels of nested maps. Works pretty well - objects get persisted nicely and the save files remains highly readable by humans.

    But without being able to set a Map as a configuration value, I'm basically looking at a major rewrite (and change in the save file format, which means I need to migrate a load of data, which means I need to simultaneously support the old and new formats... ugh).

    So what are the barriers to simply allowing a Map to be passed? Is there some way of allowing it, even in some kind of backwards compatibility mode?

    Update: I suppose I could do this myself... recursively expand the Map I get back from freezing a compound object and flatten it all into a Configuration...
     
  11. Offline

    Farlin

    I'm actually quite happy that in my plugin instead of directly making calls to the config throughout the code, I did a set of methods in my main plugin class that would do the translation of enums (includes config path, type, and default value) for option tracking and the Bukkit config API. This actually saved my butt in this instance since I only had to update about 5 methods consisting of about 20 lines of code. It was getting the order and process right that was the challenge, but very little code change was required afterwards. In instances where I needed to save Maps, I wrote my own methods and classes to export the Maps and avoided directly tying it to the Bukkit YAML API. Since SnakeYAML is included in the Bukkit libraries, it just made life a ton easier to include those libraries directly and call my own functions appropriately. Worse case, I include the SnakeYAML binaries in my plugin JAR if necessary.

    While it may seem like a lot of additional work, I can actually use these "helper" classes to create other plugins and only update those classes as Bukkit API evolves at the data layer. Naturally this does not resolve major rewrites in API due to changes in vanilla, but it certainly reduces RB update time if I can kill multiple plugins with a few classes.

    Just some thoughts to ponder as your doing those rewrites... :)
     
  12. Offline

    snikkers

    just use config.options().header("yourheaderhere");
    i hope this is right as i canĀ“t look it up right now :)

    but i have another problem
    i want to return a boolean and it worked fine with old api
    looked like this
    Code:
    Boolean someproperty = plugin.configP.getBoolean("Player." +  event.getPlayer().getPlayername() + ".someproperty", false);
    trying to get this with the new api always returns null
    if i change the path to event.getPlayer().getName() + ".someproperty" it works fine

    so is there no way to get deep values ?
    writing them is working as intended

    EDIT:
    ok got it fixed
    instead of :
    plugin.configP.getBoolean("Player." + event.getPlayer().getName() + ".someproperty", false);
    i had to use
    plugin.configP.getConfigurationSection("Player").getBoolean( event.getPlayer().getName() + ".someproperty")
     
  13. Offline

    bergerkiller

    Um I got one 'small' issue I need clarification/fixing for:
    The following function WAS like this:
    Code:
        public static void load(Configuration config, String worldname) {
                for (String type : config.getStringList(worldname + ".deniedCreatures", new ArrayList<String>())) {
                    //handle it here
                }
            }
    With the new version...it got seriously f'ed up...
    Code:
    public static void load(FileConfiguration config, String worldname) {
        for (String type : ((List<String>) config.getList(worldname + ".deniedCreatures", new ArrayList<String>())) {
            //handle it here
        }
    }
    I get an 'unchecked' warning, it's incredibly long, and I'm not even sure if getList returns a String list. How can I solve this issue? (I used the getStringList in so many locations...I'd need to write my own function if there's no alternative, which would be a shame...)

    EDIT

    Also, not possible to find out the return type by reading the default list instead? Something like:
    Code:
        public <T> List<T> getList(String path, List<T> def) {
    
        }
     
  14. Offline

    Kalman Olah

    I use this as my getStringList:
    Code:
        private static List<String> readStringList(String root) {
            YamlConfiguration config = load();
            List<String> list = new ArrayList<String>();
            for(String key:config.getConfigurationSection(root).getKeys(false)){
                list.add(key);
            }
            return list;
        }
     
  15. Offline

    bergerkiller

    @Kalman Olah Ok custom build it is...
    I'll go down the <T> road then...I'll post the final code into the Resources section.

    Just a question: So you are now forced to parse ALL value types yourself? No more auto-string-other conversion is included?

    EDIT

    It's pretty simple, but here it is:
    Code:
        @SuppressWarnings({ "unchecked", "rawtypes" })
        public <T> List<T> getList(FileConfiguration config, String path, List<T> def) {
            List list = config.getList(path);
            if (list != null && list.getClass().equals(def.getClass())) {
                return (List<T>) list;
            } else {
                return def;
            }
        }
     
  16. Offline

    iffa

    How the heck do I remove a key/property whatever now.... :Confuse:
     
  17. Offline

    Kalman Olah

    Does setting it to null not work?
     
  18. Offline

    Sagacious_Zed Bukkit Docs

  19. Offline

    Kalman Olah

    For some reason I'm getting blank lines at the top of my created config files. The amount of blank lines equal the amount of entries in the file.
    e.g.: Config.yml is 20 lines of stuff -> 20 lines of blank space at the top, before any actual text.
    Anyone know what's causing this?
     
  20. Offline

    Zaros

    I maybe losing it, but the file path goes where 'somefile' is, correct?
     
  21. Offline

    Dinnerbone Bukkit Team Member

    A handle to the File does. To get "pluginconfig.yml" in your plugins folder:
    Code:
    new File(getDataFolder(), "pluginconfig.yml");
    
     
Thread Status:
Not open for further replies.

Share This Page