1. Welcome to skUnity!

    Welcome to skUnity! This is a forum where members of the Skript community can communicate and interact. Skript Resource Creators can post their Resources for all to see and use.

    If you haven't done so already, feel free to join our official Discord server to expand your level of interaction with the comminuty!

    Now, what are you waiting for? Join the community now!

Dismiss Notice
This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.

Skript Sorting Method / Toplists

Discussion in 'Tutorials' started by AsuDev, Sep 24, 2019.

  1. AsuDev

    VIP

    Joined:
    Jan 27, 2017
    Messages:
    243
    Likes Received:
    20
    Requirements: Skript and Skript-Mirror 2.0
    Tested on: 1.8.8, 1.13.2, 1.14.4
    Difficulty: Semi-Basic/Complex

    This is a small tutorial on how to create a toplist with Skript based on a couple expressions made in skript-mirror.

    The skript-mirror expressions / sorting algorithm:
    Code (Skript):
    1. import:
    2.     java.util.ArrayList
    3.     java.util.Collections
    4.     java.util.Map$Entry
    5.     ch.njol.skript.variables.Variables
    6.  
    7. expression replace values %strings% with %strings% in %string%:
    8.     get:
    9.         set {_string} to expression-3
    10.         loop expressions-1:
    11.             add 1 to {_index}
    12.             replace all "%loop-value%" with "%{_index}th element of expressions-2%" in {_string}
    13.         return {_string}
    14.  
    15. plural expression [the] (1¦(highest|top)|2¦(lowest|last)) %integer% values of %objects% [formatted] as %string%:
    16.     get:
    17.         set {_list} to new ArrayList(Variables.getVariable(raw expressions-2.getName().toString(event), event, raw expressions-2.isLocal()).entrySet())
    18.         {_list}.sort(Entry.comparingByValue())
    19.         if parse mark = 1:
    20.             Collections.reverse({_list})
    21.         loop ...{_list}:
    22.             "%loop-value.getValue()%" is not "0"
    23.             add 1 to {_index}
    24.             set {_sorted::%{_index}%} to replace values "@place", "@index" and "@value" with "%{_index}%", "%loop-value.getKey()%" and "%loop-value.getValue()%" in expression-3
    25.             {_index} = expression-1
    26.             stop loop
    27.         return {_sorted::*}
    These expressions will allow us to make toplists. The syntax is not that hard to use and it uses a very efficient sorting algorithm. It is much faster than anything you can really make with vanilla Skript. I did some testing and this algorithm can sort up to 50,000 values in one second!

    You may be thinking, what are "@place" and "@index" and "@value" in this list. @place represents what place the value is in, for example, first place or second place. @index represents the key. For example, in a toplist, the key would be the player name or UUID. @value represents the value. For example, if a toplist was based on kills, it would be the amount of kills the player has.

    Note: it is recommended to make an auto-updator for leaderboards and not update it every time they use a command. Player's that spam a command like this could crash the server! This is just an example.
    Example toplist #1 ( Top Kills based on {Kills::*} ):
    Code (Skript):
    1. command /topkillers:
    2.     trigger:
    3.         set {_topKillers::*} to the highest (size of {Kills::*}) values of {Kills::*} as "&e@place. &b@index &7- &6@value"
    4.         message "&eTop Killers"
    5.         loop {_topkillers::*}:
    6.             message loop-value
    7.             #if loop-index is "10":
    8.             #    stop loop
    This toplist example will sort everything in {Kills::*} from highest to lowest.

    Note: it is recommended to make an auto-updator for leaderboards and not update it every time they use a command. Player's that spam a command like this could crash the server! This is just an example.
    Example toplist #2 ( Top Kills based on {Kills::*}, 10 displayed per page )
    Code (Skript):
    1. command /topkillers [<integer=1>]:
    2.     trigger:
    3.  
    4.         # Example amount to show per page. In this example, its 10
    5.         set {_showPerPage} to 10
    6.         set {_toSortTo} to arg 1 * {_showPerPage}
    7.  
    8.         set {_topKillers::*} to the highest {_toSortTo} values of {Kills::*} as "&e@place. &b@index &7- Kills: &6@value"
    9.         message "&eTop Killers Page %arg 1%"
    10.         loop {_topkillers::*}:
    11.             if ("%loop-index%" parsed as integer) is between {_toSortTo} - ({_showPerPage} - 1) and {_toSortTo}:
    12.                 message loop-value
    This toplist example will set values until a certain point (more efficient than sorting all unless you need to). This toplist uses a page system.

    Picture Example of toplist #2: Capture.PNG

    I could add a few more examples but I should have given you enough info to get started on your own. Good luck with your toplists!

    Credits: EWS (I didn't originally make this method of sorting. I edited the original version from Mirrorutils and also showed how to use it.)
     
    #1 AsuDev, Sep 24, 2019
    Last edited: Nov 11, 2019
  2. Runakai

    Runakai Well-Known Member

    Joined:
    Apr 27, 2018
    Messages:
    458
    Likes Received:
    26
    Also add that this is from Mirrorutils
     
  3. AsuDev

    VIP

    Joined:
    Jan 27, 2017
    Messages:
    243
    Likes Received:
    20
    While it is originally from Mirrorutils, I will simply state that it was originally from Mirrorutils but I am not saying that you need it as a requirement. This is a standalone piece of code that was edited and credit was given.
     
  4. BaeFell

    BaeFell I'm Poppy
    Admin

    Joined:
    Nov 27, 2016
    Messages:
    630
    Likes Received:
    219
    This is exactly what I needed! You've just saved me a lot of time reworking my old toplist code lol. Thanks @AsuDev! :emoji_heart:
     
    • Friendly Friendly x 1
  5. PuchiGFX

    PuchiGFX Member

    Joined:
    Jan 26, 2017
    Messages:
    246
    Likes Received:
    5
    upload_2019-10-20_0-52-15.png
    I get this error :-:
     
  6. AsuDev

    VIP

    Joined:
    Jan 27, 2017
    Messages:
    243
    Likes Received:
    20
  7. PuchiGFX

    PuchiGFX Member

    Joined:
    Jan 26, 2017
    Messages:
    246
    Likes Received:
    5
    Using spigot 1.8.8 and skript dev2.25
     
  8. AsuDev

    VIP

    Joined:
    Jan 27, 2017
    Messages:
    243
    Likes Received:
    20
  9. PuchiGFX

    PuchiGFX Member

    Joined:
    Jan 26, 2017
    Messages:
    246
    Likes Received:
    5
    I'll try it tomorrow :emoji_stuck_out_tongue:
    --- Double Post Merged, Oct 20, 2019, Original Post Date: Oct 20, 2019 ---
    still geting that error :/
     
  10. AsuDev

    VIP

    Joined:
    Jan 27, 2017
    Messages:
    243
    Likes Received:
    20
    I don't know what to say then. It works for me perfectly when I use those versions. Maybe someone else can clarify it.

    I actually just checked my Skript version on my 1.8.8 server and it said it was a custom version. I don't know if that makes a difference or not but you can try this one instead: https://drive.google.com/file/d/1hkiAHyGb9mGixiOVC5qehytr4QGZCRww/view

    Also I just made sure again, and sure enough it works fine on that version.
     
    #10 AsuDev, Oct 21, 2019
    Last edited: Oct 21, 2019
  11. EWS

    EWS Well-Known Member

    Joined:
    Jan 26, 2017
    Messages:
    51
    Likes Received:
    25
    Medals:
    You shouldn't sort your values directly in the command. If it takes 1 second to sort 50k variables, someone spamming the command will crash the server rather easily.
    Also, I don't understand why you replaced expr/exprs with expression. For me it only reduces readability by making lines longer.

    Here's how I usually do it in my scripts:
    Code (Skript):
    1.  
    2. function top_sort() :: strings:
    3.     clear {top::values::*}
    4.     set {top::lastsorted} to now
    5.     set {top::values::*} top 100 elements of {list::*} formatted as "<pos> <index> <value>"
    6.        
    7. function top_get() :: strings:
    8.     if difference between now and {top::lastsorted} > 10 minutes:
    9.         top_sort()
    10.     else if {top::values::*} is not set:
    11.         top_sort()
    12.     return {top::values::*}
    13.    
    14. # Use top_get() to return the top values.
    15.  
    It only sorts when the values are needed, and with a configurable delay.

    You can also further extend the sorting feature to UUIDs:
    Code (Skript):
    1.  
    2. loop top 10 elements of {list::*} formatted as "<index>;<value>":
    3.     add 1 to {_n}
    4.     set {_s::*} to split loop-value at ";"
    5.     set {_p} to {_s::1} parsed as offlineplayer
    6.     set {top::values::%{_n}%} to "%{_n}% %{_p}% %{_s::2}%"
    7.  
    Also note that what makes it sort 50k values/second is not Java's sorting method, but setting the list variable. Always use the least amount of values possible for better performance.
     
    • Like Like x 1
Loading...