Keeping a toplist

Discussion in 'Plugin Development' started by phimt, Jun 14, 2012.

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


    I've coded a simple system to store the score of all players in a flatfile, with their name, a comma, and their score. After looking around in many places, I still can't figure out the best way to keep a top list. I searched through Mcmmo's source, and found that they used 'Trees', but I feel that something simpler could be used to sort the players in descending order. Whats a good way to make a toplist? Maybe someone could point me towards a good source.

    Thanks. (I'm creating my first plugin)
  2. Offline

    Dino Filippini

    This seems to be a Java problem.

    From what I understand you're looking to take a list of information and sort it in ascending order based on a specific criteria on each line? Depending on the sample size that you plan on using this plugin for you could try a database to sort the information.

    The easiest method that comes to mind is that whenever someone's score changes you compare it to the other scores in the file to keep a constantly ordered list from the get-go. If you already have a full list of data, then you'll have to sort it first then take care of it on the fly with the previous idea.

    This is my first week designing plugins for bukkit as well, so if someone else would like to verify that the above idea is valid and won't take centuries for the machine to execute, that'd be grand. ;)
  3. Offline


    If you're gonna keep using a flat file (which would become really inefficient for servers with a large player base), I would recommend keeping the file sorted as you go:

    2. public void storePlayerScore(Player p, int score) {
    3. File f = new File("plugins::YourPlugin::scores.yml".replace("::", File.separator));
    5. if (!f.exists)
    6. try {
    7. f.createNewFile();
    8. } catch (IOException e) {
    9. e.printStackTrace();
    10. } finally {
    11. if (!f.exists)
    12. return;
    13. }
    14. }
    16. Scanner scanner = new Scanner(f);
    17. String data = "";
    18. boolean hasWritten = false;
    20. while (scanner.hasNextLine()) {
    21. String[] lineSplit = scanner.nextLine().split(",").trim();
    23. try {
    24. if (Integer.parseInt(lineSplit[1]) <= score) {
    25. if (data.length() > 0)
    26. data = data.trim() + "\n";
    28. data += p.getName() + "," + score;
    30. hasWritten = true;
    31. } catch (NumberFormatException e) {
    32. e.printStackTrace();
    33. }
    35. data += lineSplit[0] + "," + lineSplit[1];
    36. }
    38. scanner.close();
    40. if (!hasWritten)
    41. data = data.trim() + "\n" + p.getName() + "," + score;
    44. try {
    45. out = new FileOutputStream(f);
    47. out.write(data.getBytes());
    48. } catch (IOException e) {
    49. e.printStackTrace();
    50. } finally {
    51. if (out != null)
    52. out.close();
    53. }
    54. }

    This way, you just insert the user's data directly above the first score less then theirs. For your toplist, now all you'll have to do is read the top x number of lines.
  4. File f = new File("plugins::YourPlugin::scores.yml".replace("::", File.separator));
    is wrong, use
    File f = new File(this.getDataFolder(),"scores.yml");
  5. Offline


    Well, not "wrong", to be accurate. It's just an habit, I like knowing exactly where my program is pulling information from. Either will work.
  6. your methode may not work if the bukkit team desides to chance the plugin folder to addons, my will still work, also, whit bukkit.yml you can chance the directory of the plugins already, so your methode wont work on those servers
  7. Offline


    Again, it WILL work, because it'll create the directory structure and required file when it's executed. It won't be pretty and reside with all the other plugins, but it's still a valid statement.
  8. Offline


    If your data is in a hash table, there is another way to go about this that wont hammer your disk.
    This code will create a temporary sort tree, sorted by the 'value' of the hash table {key,value} pair.

    The code will leave the base hashmap untouched once you are doing what ever you want with the sorted data.

    To preserve the data over a server reset, the hash tables need only to be read/saved to the disk on server enable and disable.

    //Import some java classes:
        import java.util.HashMap;
        import java.util.TreeMap;
    //Declare your hashmap (I am assuming Sting = playerName, value to sort is a Long number)
    public HashMap<String, Long> map = new HashMap<String,Long>();
    public static void sortPlayers( )
            ValueComparator bvc =  new ValueComparator(map);
            @SuppressWarnings({ "unchecked", "rawtypes" })
            // Declare a temporary sort tree
            TreeMap<String,Long> sorted_map = new TreeMap(bvc);
            // put your playermap into the sort tree
            // exit if nothing in the map to sort
            if (sorted_map.size() == 0) {
            // get first key (player name) in the sorted map
            String key2 = sorted_map.firstKey();
            // for all items in the sorted map
            for (int i=0; i<sorted_map.size(); i++) {
                long sortValue = sorted_map.get(key2);
                // key2 is a string and should be the playerName
                // i = the sorted order position
                // sortValue = the value that was sorted to get the order
                // get the name of the next player in the sorted list
                // this should be last statement for the 'for loop'
                key2 = sorted_map.higherKey(key2);
    You need a separate class for the comparitor, this one will sort 'Long' values highest to lowest
    import java.util.Comparator;
    import java.util.Map;
    class ValueComparator implements Comparator {
          Map base;
          public ValueComparator(Map base) {
              this.base = base;
          public int compare(Object a, Object b) {
            if((Long)base.get(a) < (Long)base.get(b)) {
              return 1;
            } else if((Long)base.get(a) == (Long)base.get(b)) {
              return 0;
            } else {
              return -1;
  9. Offline


    Thanks for your replies, I ended up getting it to work using Edge209's suggestion, as he correctly guessed I was using a hash table to store the data. I'll probably end up keeping the file sorted or move to a database later.
Thread Status:
Not open for further replies.

Share This Page