Other [OUTDATED] Don't use YAML.

  • 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!

Status
Not open for further replies.

Rezz

Addon Developer
Jan 24, 2017
80
37
18
WARNING
see https://forums.skunity.com/threads/outdated-dont-use-yaml.3850/page-2#post-57240


Unfortunately, YAML is one of the most inefficient things you can do in Skript. It may seem like a convenience -- or even a necessary enhancement -- at first glance, but the harsh reality is: no existing addon gets it right.

Why use YAML?

Before we dive deep into the dirty details of Skript’s disgracefully bad YAML support, let’s first understand why it’s used despite Skript’s built-in configurable options. Skript’s goal is to give absolutely anyone, regardless of prior programming knowledge, the ability to modify and customize their server. In fact, Skript’s level of entry is so low many people inevitably find themselves picking it up as their first programming language (whether or not that’s a good thing is a debate for another day). Those who eventually become comfortable with their scripting abilities then try to emulate regular Java plugins as much as possible, and this is where YAML comes to mind.

YAML is the configuration file format officially supported by Bukkit (and its forks, like Spigot), which makes YAML an obvious choice for plugins wanting easy-to-implement and easy-to-manage configs; a majority of plugins use ‘config.yml’ as their sole means of customization. So any script author who wants to create an accurate plugin illusion would use ‘config.yml’ too, right?

What’s wrong with YAML?

The problem with YAML in Skript is that every addon that supports it parses YAML files every single time their syntax is used. In other words, whenever you retrieve a single value from a YAML file, ALL bytes are read, ALL values are parsed, and EVERYTHING but the specific value you were looking for is immediately thrown away. This is the exact opposite of how YAML is used by actual plugins; YAML files aren’t parsed over and over again, they’re parsed once and stored in memory -- much like Skript’s own variables.

See for yourself:

All three addons behave in the same way: parse the entire file, get the value, and discard everything. Again, this is the exact, polar opposite of normal plugin behavior. It’s also terrible for performance; reading a file from disk isn’t a light operation, but then also parsing its contents every single time is icing on the cake. Using YAML in any of your scripts (in its current state, at least) is a guaranteed performance cut.

How bad is it?

I created a simple test script to demonstrate the performance of Skript variables versus YAML files. The data speaks for itself.

Generating 3000 random values, and retrieving 500 random values:
2ehqsjl.png

Generating 2500 random values, and retrieving 2500 random values:
DXjfq3Z.png

The script:
code_language.skript:
#
#   Variables vs. YAML Test
#   -----------------------
#   By: RezzedUp
#   -----------------------
#   Requires:
#   - Skript 2.2 (functions)
#   - SkUtilities (yaml & files)
#

#
#   Change the values below to test different scenarios.
#
#   PAUSE: How long the script should wait when it encounters its 'pause' state.
#
#   *_TESTS: The number of tests to run. This value modifies the number of unique
#            values generated for 'set' tests, and how many values are randomly
#            selected for 'get' tests.
#
#   *_PAUSE: The number of tests to run before the script should enter its 'pause' state.
#            This prevents the server from crashing (because YAML is incredibly inefficient),
#            but please remember that this will also increase test-time.
#

options:

    PAUSE: wait 2 ticks
 
    SET_TESTS: 2500
    SET_PAUSE: 250
 
    GET_TESTS: 2500
    GET_PAUSE: 250
 
on script load:

    delete {test::*}
 
on script unload:

    delete {test::*}

function randomString(length: integer = 20, alphabet: string = "abcdefghijklmnopqrstuvwxyz0123456789-_") :: string:

    set {_chars::*} to {_alphabet} split at ""
    set {_string} to ""
 
    while length of {_string} is less than {_length}:
 
        set {_string} to "%{_string}%%random element out of {_chars::*}%"
     
    return {_string}
 
command /test:
    trigger:
 
        delete {test::*}
     
        send "&aSetting {@SET_TESTS} variables..."
     
        set {_var-set-start} to now
        set {_var-set-iterations} to 0
     
        while {_var-set-iterations} is less than {@SET_TESTS}:
     
            set {_key} to randomString()
         
            {test::%{_key}%} isn't set
         
            set {test::%{_key}%} to {_key}
         
            add 1 to {_var-set-iterations}
         
            mod({_var-set-iterations}, {@SET_PAUSE}) is 0
         
            {@PAUSE}
         
            send "Set %{_var-set-iterations}% variables. %difference between now and {_var-set-start}% since starting."
         
        set {_var-set-time} to difference between now and {_var-set-start}
        send "&6Variable-set test:&f %{_var-set-time}%"
         
        if file "test.yml" exists:
            delete file "test.yml"
        create file "test.yml"
         
        send "&aSetting {@SET_TESTS} YAML values..."
         
        set {_yaml-set-start} to now
        set {_yaml-set-iterations} to 0
     
        loop {test::*}:
     
            set yaml value loop-index from file "test.yml" to loop-value
            add 1 to {_yaml-set-iterations}
         
            mod({_yaml-set-iterations}, {@SET_PAUSE}) is 0
         
            {@PAUSE}
         
            send "Set %{_yaml-set-iterations}% YAML values. %difference between now and {_yaml-set-start}% since starting."
         
        set {_yaml-set-time} to difference between now and {_yaml-set-start}
        send "&6YAML-set test: &f%{_yaml-set-time}%"
     
        #
        #   GET TESTS
        #
     
        send "&aTesting {@GET_TESTS} random variables"
     
        set {_var-get-start} to now
     
        loop {@GET_TESTS} times:
     
            set {_var-get-key} to a random element out of {test::*}
            set {_var-get-value} to {test::%{_var-get-key}%}
         
            mod(loop-number, {@GET_PAUSE}) is 0
         
            {@PAUSE}
     
            send "Got %loop-number% values. %difference between now and {_var-get-start}% since starting."
     
        set {_var-get-time} to difference between now and {_var-get-start}
        send "&6Variable-get test: &f%{_var-get-time}%"
     
        send "&aTesting {@GET_TESTS} random YAML values"
     
        set {_yaml-get-start} to now
     
        loop {@GET_TESTS} times:
     
            set {_yaml-get-key} to a random element out of {test::*}
            set {_yaml-get-value} to yaml value {_yaml-get-key} from file "test.yml"
         
            mod(loop-number, {@GET_PAUSE}) is 0
         
            {@PAUSE}
     
            send "Got %loop-number% YAML values. %difference between now and {_yaml-get-start}% since starting."
             
        set {_yaml-get-time} to difference between now and {_yaml-get-start}
        send "&6YAML-get test: &f%{_yaml-get-time}%"
     
        send "&f--- RESULTS: ---"
        send "&bSet {@SET_TESTS} variables test:&f %{_var-set-time}%"
        send "&3set {@SET_TESTS} YAML values test: &f%{_yaml-set-time}%"
        send "&bGet {@GET_TESTS} variables test: &f%{_var-get-time}%"
        send "&3Get {@GET_TESTS} YAML values test: &f%{_yaml-get-time}%"

What variables can do in seconds takes minutes in YAML. Also, notice how the tests must 'pause' every so often: YAML in Skript is so inefficient, this test will crash the server without taking breaks.

What should be used instead?

Use Skript’s built-in tools, like options and variables. The only legitimate use case for YAML in Skript is checking other plugins’ configs; anything else should be off limits.

More info will be added later (work in progress).
 
Last edited by a moderator:
I'm currently am making a guild plugin, for that I am using yml (skultilities), so I cannot use variables because I cannot store more than one information inside, maybe we can, but due to my knowledge I found yaml more comfortable for storing guild name, allies, enemies, home, chunks, members, moderator and master.

Though this might be troublesome for me in the future, if I have too many players on my server.

Anyways, thanks for sharing this with us. Good thing to know!
 
  • Like
Reactions: Rezz
I'm currently am making a guild plugin, for that I am using yml (skultilities), so I cannot use variables because I cannot store more than one information inside, maybe we can, but due to my knowledge I found yaml more comfortable for storing guild name, allies, enemies, home, chunks, members, moderator and master.

Though this might be troublesome for me in the future, if I have too many players on my server.

Anyways, thanks for sharing this with us. Good thing to know!
Can't you use this?

{guilds::GUILDNAME::allies::*}
{guilds::GUILDNAME::enemies::*}
{guilds::GUILDNAME::homes::*}
{guilds::GUILDNAME::chunks::*}
{guilds::GUILDNAME::master::*}
{guilds::GUILDNAME::mods::*}
{guilds::GUILDNAME::member::*}

I made a mail system for my server that stores like this:

{mail::MAILOWNER::MAILSENDER::MESSAGE}

So the players can receive up to 4 messages from another player (this cap I made to avoid spam and to don't let the variable get too big.

Here is the code if you want to take a look, but it's in portuguese (the messages), but the idea you can get from it:

code_language.skript:
command /mail [<text>] [<text>] [<text>]:
  trigger:
    if {mail.%player%} is not set:
      set {mail.%player%} to true
      set {mail::%player%} to "%player%"
    if arg 1 is not set:
      if size of {mail::%player%::*} = 0:
        message "[&cMail&r] &aVocê não tem nenhuma mensagem."
      if size of {mail::%player%::*} >= 1:
        message "[&cMail&r] &aVocê tem mensagens de &e&n%size of {mail::%player%::*}%&r&a remetente(s)!"
        message "[&cMail&r] &aDigite &d/mail ler &apara ler as mensagens"
      message "[&cMail&r] &aUse &d/mail &chelp &apara informações."
      stop
    if arg 1 is "help" or "ajuda":
      if arg 2 is "ler":
        message " "
        message "[&cMail&r] &aAo usar o comando &d/mail &cler&a, o plugin irá checkar se você possui mensagens em sua caixa de entrada."
        message "[&cMail&r] &aCaso existam mensagens na sua caixa de mensasgem, irão aparecer a lista de remetentes."
        message "[&cMail&r] &aPara ler as mensagens, será necessário digitar &d/mail ler &cNICK&a, substituindo &cNICK&a pelo nick do player da lista de remetentes."
        message "[&cMail&r] &6É possível imprimir as mensagens recebidas. Veja mais sobre digitando &d/mail help &cimprimir"
        stop
      if arg 2 is "imprimir":
        message " "
        message "[&cMail&r] &aAo usar o comando &d/mail &cimprimir&a, o plugin irá checkar se você possui mensagens em sua caixa de entrada."
        message "[&cMail&r] &aCaso existam mensagens na sua caixa de mensasgem, o plugin te lembrará de ter um livro em mãos para imprimir, no livro, as mensagens."
        message "[&cMail&r] &aCom os livros em mãos, digite &d/mail imprimir &apara armazenar em um livro todas as mensagens"
        message "[&cMail&r] &6OBS: &aesse comando seta uma página por mensagem, com no máximo de 50 páginas por livro, há o limite de 50 mensagens impressas num livro."
        message "[&cMail&r] &6Esse comando não irá limpar sua caixa de mensagens. Veja mais sobre digitando &d/mail help &capagar"
        stop
      if arg 2 is "apagar":
        message " "
        message "[&cMail&r] &aAo usar o comando &d/mail apagar&a, o plugin irá checkar se você possui mensagens em sua caixa de entrada."
        message "[&cMail&r] &aCaso existam mensagens na sua caixa de mensasgem, será possível apagar todas as mensagens de um remetente."
        message "[&cMail&r] &aPara apagar todas as mensagens de um remetende, digite &d/mail apagar &cNICK&a"
        message "[&cMail&r] &aVocê pode apagar todas as mensagens recebidas digitando &d/mail apagar &ctudo&a"
        message "[&cMail&r] &6Saiba como armazenar mensagens digitando &d/mail help &cimprimir"
        stop
      if arg 2 is "enviar":
        message " "
        message "[&cMail&r] &aAo usar o comando &d/mail &cenviar&a, informações sobre como enviar mensagens serão mostradas."
        message "[&cMail&r] &aPara enviar uma mensagem, utilize o comando &d/mail enviar &dNICK MENSAGEM"
        message "[&cMail&r] &aPara entender os limites, digite &d/mail help &climites"
        stop
      if arg 2 is "limites":
        message " "
        message "[&cMail&r] &aCada mensagem tem o limite de 150 caracteres."
        message "[&cMail&r] &aComo forma de evitar abusos, é possivel enviar até 4 mensagens para cada player. Só é possível enviar mensagens para jogadores que logaram no servidor após a implementação do e-mail"
        message "[&cMail&r] &aAo enviar 4 mensagens para um player, você ficará impedido de enviar mensagem para este player até que o mesmo limpe a caixa de entrada"
        message "[&cMail&r] &aPor isso, é importante manter sua caixa de mensagens limpa. Saiba mais digitando &d/mail help &capagar"
        message "[&cMail&r] &aÉ possível armazenar as mensagens em um livro. Veja sobre digitando &d/mail help &cimprimir"
        stop
      message "[&cMail&r] &d/mail help &cler - &6Informações sobre leitura de mensagens."
      message "[&cMail&r] &d/mail help &capagar - &6Informações sobre como apagar mensagens."
      message "[&cMail&r] &d/mail help &cimprimir - &6Informações sobre como transcrever para livros as mensagens."
      message "[&cMail&r] &d/mail help &cenviar - &6Informações sobre como enviar uma mensagem para um player."
      message "[&cMail&r] &d/mail help &climites - &6Informações sobre os limites de mensagem."
      stop
    if arg 1 is "ler" or "read":
      if size of {mail::%player%::*} = 0:
        message "[&cMail&r] &aVocê não tem nenhuma mensagem."
        stop
      if arg 2 is set:
        loop {mail::%player%::*}:
          if loop-index = arg 2:
            set {_loop} to size of {mail::%player%::%arg 2%::*}
          set {_num} to 0
          loop {_loop} times:
            add 1 to {_num}
            wait 2 tick
            message "[&cMail&r] %{mail::%player%::%arg 2%::%{_num}%}%"
          wait 2 tick
          message "[&cMail&r] &aPara apagar essas mensagens, digite &d/mail apagar &c%arg 2%"
          stop
      if size of {mail::%player%::*} >= 1:
        message "[&cMail&r] &aRemetentes: &d%{mail::%player%::*}%"
        message "[&cMail&r] &aDigite &d/mail ler &cNICK &a para ler as mensagens enviadas por um anão da lista acima."
        stop
    if arg 1 is "imprimir" or "print":
      message "[&cMail&r] &cTemporariamente desativado"
      stop
#      if size of {mail::%player%::*} = 0:
#        message "[&cMail&r] &aVocê não tem nenhuma mensagem para imprimir!"
#        stop
#      if player's tool is a book:
#        remove 1 book from the player's tool
#        set {_pages} to 0
#        loop {mail::%player%::*}:
#          loop {mail::%player%::%loop-value%::*}:
#            add 1 to {_pages}
#            if {_pages} = 50:
#              message "[&cMail&r] &cERRO! A impressão é limitada em 50 páginas. Parando impressão."
#              exit 3 sections
#            add a page with data "%loop-value-2%" to book {_book}
#        give player 1 of {_book}
#        stop
      message "[&cMail&r] &aTenha em mãos um livro em branco e digite &d/mail imprimir &a novamente"
      stop
    if arg 1 is "apagar" or "delete":
      if size of {mail::%player%::*} = 0:
        message "[&cMail&r] &aVocê não tem nenhuma mensagem."
        stop
      if arg 2 is not set:
        message "[&cMail&r] &aVocê pode receber até 4 mensagens de cada player"
        message "[&cMail&r] &aÉ importante manter sempre a caixa de entrada limpa para poder receber mensagens"
        message "[&cMail&r] &aReceber 4 mensagens de um player não impede de receber mensagens de outros, mas apenas deste que lhe enviou 4 mensagens"
        message "[&cMail&r] &aPara apagar todas as mensagens, independente de quem tenha enviado, digite &d/mail apagar tudo"
        message "[&cMail&r] &aPara apagar as mensagens recebidas de um player, digite &d/mail apagar &cNICK"
        stop
      if arg 2 is "tudo":
        clear {mail::%player%::*}
        message "[&cMail&r] &aTodas as mensagens foram apagadas!"
        stop
      if arg 2 is set:
        loop {mail::%player%::*}:
          if loop-index = arg 2:
            clear {mail::%player%::%arg 2%::*}
            clear {mail::%player%::%arg 2%}
            message "[&cMail&r] &aMensagens recebidas de %arg 2% foram apagadas!"
            stop
    if arg 1 is "enviar" or "send" or "s":
      if arg 2 is not set:
        message "[&cMail&r] &aO limite de 4 mensagens é individual, vide &d/mail help &climites"
        message "[&cMail&r] &aUse o comando &d/mail enviar &cNICK &eMENSAGEM AQUI &apara enviar uma mensagem."
        stop
      if arg 2 is set:
        if arg 2 = player:
          message "[&cMail&r] &cERRO! &fVocê está tentando enviar uma mensagem para você mesmo!"
          stop
        if {mail.%arg 2%} is true:
          set {_encontrado} to true
          if {mail::%arg 2%::%player%} is not set:
            set {mail::%arg 2%::%player%} to "%player%"
        if {_encontrado} is not set:
          message "[&cMail&r] &cERRO! &fEsse jogador ainda não teve sua caixa de e-mail ativada."
          stop
        if size of {mail::%arg 2%::%player%::*} = 4:
          message "[&cMail&r] &cERRO! Já existem 4 mensagens suas lidas e/ou não apagadas na caixa do seu remetente"
          message "[&cMail&r] &aLembre-o de apagar as suas mensagens usando o comando &d/mail apagar %player%"
          message "[&cMail&r] &aLeia sobre os limites usando &d/mail help &climites"
          stop
        if arg 3 is not set:
          message "[&cMail&r] &cERRO! Mensagem em branco."
          stop
        if arg 3 is set:
          set {_check} to length of arg 3
          if {_check} < 6:
            message "[&cMail&r] &cERRO! Spam detectado!"
            log "%player% TENTOU enviar ao %arg 2% (razao: <6): %arg 3%" to "Mail/mail.log"
            stop
          if {_check} > 151:
            message "[&cMail&r] &cERRO! Limite de 150 caracteres por mensagem"
            log "%player% TENTOU enviar ao %arg 2% (razao: >150): %arg 3%" to "Mail/mail.log"
            stop
          add "&9%player% diz: &7&o%arg 3%" to {mail::%arg 2%::%player%::*}
          log "%player% enviou para o %arg 2%: %arg 3%" to "Mail/mail.log"
          message "[&cMail&r] &aParabéns, &b%player%&a, você enviou uma mensagem para o &d%arg 2%&a."
          if size of {mail::%arg 2%::%player%::*} = 4:
            message "[&cMail&r] &aVocê já enviou &d4&a mensagens para esse remetente. Este é o limite de mensagens. Saiba mais digitando &d/mail help limites"
            stop
          if size of {mail::%arg 2%::%player%::*} = 3:
            message "[&cMail&r] &aVocê já enviou &d3 &ade &d4 &amensagens para esse remetente. Saiba mais digitando &d/mail help limites"
            stop
          if size of {mail::%arg 2%::%player%::*} = 2:
            message "[&cMail&r] &aVocê já enviou &d2 &ade &d4 mensagens para esse remetente. Saiba mais digitando &d/mail help limites"
            stop
          message "[&cMail&r] &aVocê já enviou &d1 &ade &d4 mensagens para esse remetente. Saiba mais digitando &d/mail help limites"
          stop
      message "[&cMail&r] &cERRO! &aDigite &d/mail enviar &cNICK &eMENSAGEM AQUI"
      stop
    message "[&cMail&r] &cERRO! &aComando inválido! Digite &d/mail help &apara ver os comandos disponíveis!"
 
Why would you want to do that? Just give your players the access to the right commands and let them do their stuff, create factions, add players, remove players, etc.

You could log the creation and the modifications of clans by logs, like I log all messages sent via my mails system:

my log sytem - very simple:
code_language.skript:
log "%player% sent a mail to %arg 2%: %arg 3%" to "Mail/mail.log"
%player% is the one who used the command.
%arg 2% is the receipt of the message.
%arg 3% is the message itself.

I save it all to a single log.

But you could save to diferent logs, each for each receipt doing this:
code_language.skript:
log "%player% sent a mail to %arg 2%: %arg 3%" to "Mail/%ARG2%/mail.log"
Look that I added another folder. There's the mail, inside will be created a folder for the receipt %arg 2%, and inside, ALL messages send from any player to the %arg 2% would be logged.

in your case, here is an exemple:

creation of guild:
code_language.skript:
log "%player% created FACTIONAME" to "guilds/FACTIONNAME/factionhistory.log"

add of player:
code_language.skript:
log "%player% added %PLAYERNAME / ARG X% to the FACTIONNAME" to "guilds/FACTIONNAME/factionhistory.log"

removal of a player:
code_language.skript:
log "%player% removed %PLAYERNAME / ARG X% from the FACTIONNAME" to "guilds/FACTIONNAME/factionhistory.log"

etc.

But it's up to you.
 
I wish there is a Dislike button...

I don't understand why youre saying "DON'T USE YAML"

Just let the user what he like. example i work evertime with yaml and i have no problem with loading variables?


I got already a Full Remade SkyBlock without any Variables, all is with YAML Configure. And i have no problem with it (No laggs or somethings).

I wish this post will be removed.

Let the user what he want...
 
  • Like
Reactions: Goetheus
Sorry, but I can't agree with that.

I need to somewhere store a large quantities of data. Would you recommend to store 4000 chunk files, 3500 playedata files in variables? From my personal testing, yaml is bad only if:
  • You store thousands of lines of data in one file
  • Create unneeded folders for, in example, every player
  • Use yaml data in operations where the data repeats and doesn't change at all
With all that said, I would recommend this to everyone who uses yaml:
  1. If possible, store in separete files than in one file. For example, don't store playerdata in one file, create separete file for every player, it will be much easier for skript to find file than player in thousands of lines
  2. If you have data like configuration values in yaml file, free feel to place them in variables on script load
  3. Try to minimise yaml usage, for example, I've seen coordinates stored in 5 lines, it is possible to store them in one line too..
  4. Run tests before public release. Every unneeded yaml usage can lower perfomance, try to think about every operation from large perapective..
I hope I covered most of my thoughts, they might be wrong, this is from my experience with 50-100 players activly playing in server that uses yaml in most of operations.
 
  • Like
Reactions: Goetheus and Rezz
I wish there is a Dislike button...

I don't understand why youre saying "DON'T USE YAML"
Did you read the post? I think I explain myself thoroughly.

Just let the user what he like. example i work evertime with yaml and i have no problem with loading variables?
No, you do have a problem. YAML in Skript is too inefficient; ignoring it (or deciding that you don't care about its terrible performance) doesn't make the problem go away.

I got already a Full Remade SkyBlock without any Variables, all is with YAML Configure. And i have no problem with it (No laggs or somethings).
That's horrifying. YAML isn't a replacement for variables.

I wish this post will be removed.
This thread is simply warning people about how using YAML in Skript will negatively impact your server's performance. Why remove it?

Let the user what he want...
I can't force anyone to stop using YAML. You're free to use it as much as you like. :emoji_stuck_out_tongue_closed_eyes:


- - - - - - - - - - - - - -


Sorry, but I can't agree with that.

I need to somewhere store a large quantities of data. Would you recommend to store 4000 chunk files, 3500 playedata files in variables?
Yes. This is what variables were made for.

From my personal testing, yaml is bad only if:
  • You store thousands of lines of data in one file
  • Create unneeded folders for, in example, every player
  • Use yaml data in operations where the data repeats and doesn't change at all
YAML in Skript will always be bad as long as its syntaxes parse files every single time they're used. The moment an addon creates a parse-once solution, YAML will finally be viable.

With all that said, I would recommend this to everyone who uses yaml:
  1. If possible, store in separete files than in one file. For example, don't store playerdata in one file, create separete file for every player, it will be much easier for skript to find file than player in thousands of lines
  2. If you have data like configuration values in yaml file, free feel to place them in variables on script load
  3. Try to minimise yaml usage, for example, I've seen coordinates stored in 5 lines, it is possible to store them in one line too..
  4. Run tests before public release. Every unneeded yaml usage can lower perfomance, try to think about every operation from large perapective..
These are great tips in general, thanks for your input. :emoji_smile:
 
Did you read the post? I think I explain myself thoroughly.


No, you do have a problem. YAML in Skript is too inefficient; ignoring it (or deciding that you don't care about its terrible performance) doesn't make the problem go away.


That's horrifying. YAML isn't a replacement for variables.


This thread is simply warning people about how using YAML in Skript will negatively impact your server's performance. Why remove it?


I can't force anyone to stop using YAML. You're free to use it as much as you like. :emoji_stuck_out_tongue_closed_eyes:


- - - - - - - - - - - - - -



Yes. This is what variables were made for.


YAML in Skript will always be bad as long as its syntaxes parse files every single time they're used. The moment an addon creates a parse-once solution, YAML will finally be viable.


These are great tips in general, thanks for your input. :emoji_smile:
Of course variables will be faster in any case, but it is SO MUCH easier to modify yaml files than varables..
 
  • Like
Reactions: Rezz
I agree that JSON is a lot better than YAML but it is still not optimal. Here are three issues:

- You need SkUtilities
- The output of "variable to JSON" returns a one line representation of the JSON
- JSON is ugly (and error prone if you try to write/change things)
- JSON does not allow comments (kind of stupid if you want to make config files...)

skript-json is only good if you never have to read/change the data.


YAML is in itself not a bad idea and definitely a good choice for configs. We just lack an
addon with a decent implementation. Here are the features that are currently missing:

- Load YAML file to list variable
- Save list variable to YAML file (without overriding the comments)


And for everyone who wants to use YAML as a database: DON'T!
YAML is for config files and nothing else.
 
  • Like
Reactions: aescraft
I agree that JSON is a lot better than YAML but it is still not optimal. Here are three issues:

- You need SkUtilities
- The output of "variable to JSON" returns a one line representation of the JSON
- JSON is ugly (and error prone if you try to write/change things)
- JSON does not allow comments (kind of stupid if you want to make config files...)
So sad that skript-json doesn't support hjson.
 
hmm this is good information however options can't be called in different scripts and using variables would fill up the .cvs file if storing a large amount of information which would lead to performance drop anyway
 
Unfortunately, YAML is one of the most inefficient things you can do in Skript. It may seem like a convenience -- or even a necessary enhancement -- at first glance, but the harsh reality is: no existing addon gets it right.

Why use YAML?

Before we dive deep into the dirty details of Skript’s disgracefully bad YAML support, let’s first understand why it’s used despite Skript’s built-in configurable options. Skript’s goal is to give absolutely anyone, regardless of prior programming knowledge, the ability to modify and customize their server. In fact, Skript’s level of entry is so low many people inevitably find themselves picking it up as their first programming language (whether or not that’s a good thing is a debate for another day). Those who eventually become comfortable with their scripting abilities then try to emulate regular Java plugins as much as possible, and this is where YAML comes to mind.

YAML is the configuration file format officially supported by Bukkit (and its forks, like Spigot), which makes YAML an obvious choice for plugins wanting easy-to-implement and easy-to-manage configs; a majority of plugins use ‘config.yml’ as their sole means of customization. So any script author who wants to create an accurate plugin illusion would use ‘config.yml’ too, right?

What’s wrong with YAML?

The problem with YAML in Skript is that every addon that supports it parses YAML files every single time their syntax is used. In other words, whenever you retrieve a single value from a YAML file, ALL bytes are read, ALL values are parsed, and EVERYTHING but the specific value you were looking for is immediately thrown away. This is the exact opposite of how YAML is used by actual plugins; YAML files aren’t parsed over and over again, they’re parsed once and stored in memory -- much like Skript’s own variables.

See for yourself:

All three addons behave in the same way: parse the entire file, get the value, and discard everything. Again, this is the exact, polar opposite of normal plugin behavior. It’s also terrible for performance; reading a file from disk isn’t a light operation, but then also parsing its contents every single time is icing on the cake. Using YAML in any of your scripts (in its current state, at least) is a guaranteed performance cut.

How bad is it?

I created a simple test script to demonstrate the performance of Skript variables versus YAML files. The data speaks for itself.

Generating 3000 random values, and retrieving 500 random values:
2ehqsjl.png

Generating 2500 random values, and retrieving 2500 random values:
DXjfq3Z.png

The script:
code_language.skript:
#
#   Variables vs. YAML Test
#   -----------------------
#   By: RezzedUp
#   -----------------------
#   Requires:
#   - Skript 2.2 (functions)
#   - SkUtilities (yaml & files)
#

#
#   Change the values below to test different scenarios.
# 
#   PAUSE: How long the script should wait when it encounters its 'pause' state.
#
#   *_TESTS: The number of tests to run. This value modifies the number of unique
#            values generated for 'set' tests, and how many values are randomly
#            selected for 'get' tests.
#
#   *_PAUSE: The number of tests to run before the script should enter its 'pause' state.
#            This prevents the server from crashing (because YAML is incredibly inefficient),
#            but please remember that this will also increase test-time.
#

options:

    PAUSE: wait 2 ticks
  
    SET_TESTS: 2500
    SET_PAUSE: 250
  
    GET_TESTS: 2500
    GET_PAUSE: 250
  
on script load:

    delete {test::*}
  
on script unload:

    delete {test::*}

function randomString(length: integer = 20, alphabet: string = "abcdefghijklmnopqrstuvwxyz0123456789-_") :: string:

    set {_chars::*} to {_alphabet} split at ""
    set {_string} to ""
  
    while length of {_string} is less than {_length}:
  
        set {_string} to "%{_string}%%random element out of {_chars::*}%"
      
    return {_string}
  
command /test:
    trigger:
  
        delete {test::*}
      
        send "&aSetting {@SET_TESTS} variables..."
      
        set {_var-set-start} to now
        set {_var-set-iterations} to 0
      
        while {_var-set-iterations} is less than {@SET_TESTS}:
      
            set {_key} to randomString()
          
            {test::%{_key}%} isn't set
          
            set {test::%{_key}%} to {_key}
          
            add 1 to {_var-set-iterations}
          
            mod({_var-set-iterations}, {@SET_PAUSE}) is 0
          
            {@PAUSE}
          
            send "Set %{_var-set-iterations}% variables. %difference between now and {_var-set-start}% since starting."
          
        set {_var-set-time} to difference between now and {_var-set-start}
        send "&6Variable-set test:&f %{_var-set-time}%"
          
        if file "test.yml" exists:
            delete file "test.yml"
        create file "test.yml"
          
        send "&aSetting {@SET_TESTS} YAML values..."
          
        set {_yaml-set-start} to now
        set {_yaml-set-iterations} to 0
      
        loop {test::*}:
      
            set yaml value loop-index from file "test.yml" to loop-value
            add 1 to {_yaml-set-iterations}
          
            mod({_yaml-set-iterations}, {@SET_PAUSE}) is 0
          
            {@PAUSE}
          
            send "Set %{_yaml-set-iterations}% YAML values. %difference between now and {_yaml-set-start}% since starting."
          
        set {_yaml-set-time} to difference between now and {_yaml-set-start}
        send "&6YAML-set test: &f%{_yaml-set-time}%"
      
        #
        #   GET TESTS
        #
      
        send "&aTesting {@GET_TESTS} random variables"
      
        set {_var-get-start} to now
      
        loop {@GET_TESTS} times:
      
            set {_var-get-key} to a random element out of {test::*}
            set {_var-get-value} to {test::%{_var-get-key}%}
          
            mod(loop-number, {@GET_PAUSE}) is 0
          
            {@PAUSE}
      
            send "Got %loop-number% values. %difference between now and {_var-get-start}% since starting."
      
        set {_var-get-time} to difference between now and {_var-get-start}
        send "&6Variable-get test: &f%{_var-get-time}%"
      
        send "&aTesting {@GET_TESTS} random YAML values"
      
        set {_yaml-get-start} to now
      
        loop {@GET_TESTS} times:
      
            set {_yaml-get-key} to a random element out of {test::*}
            set {_yaml-get-value} to yaml value {_yaml-get-key} from file "test.yml"
          
            mod(loop-number, {@GET_PAUSE}) is 0
          
            {@PAUSE}
      
            send "Got %loop-number% YAML values. %difference between now and {_yaml-get-start}% since starting."
              
        set {_yaml-get-time} to difference between now and {_yaml-get-start}
        send "&6YAML-get test: &f%{_yaml-get-time}%"
      
        send "&f--- RESULTS: ---"
        send "&bSet {@SET_TESTS} variables test:&f %{_var-set-time}%"
        send "&3set {@SET_TESTS} YAML values test: &f%{_yaml-set-time}%"
        send "&bGet {@GET_TESTS} variables test: &f%{_var-get-time}%"
        send "&3Get {@GET_TESTS} YAML values test: &f%{_yaml-get-time}%"

What variables can do in seconds takes minutes in YAML. Also, notice how the tests must 'pause' every so often: YAML in Skript is so inefficient, this test will crash the server without taking breaks.

What should be used instead?

Use Skript’s built-on tools, like options and variables. The only legitimate use case for YAML in Skript is checking other plugins’ configs; anything else should be off limits.

More info will be added later (work in progress).
Thanks for posting this. Maybe not too fitting in tutorials but it doesn't fit all that much anywhere else either.
hmm this is good information however options can't be called in different scripts and using variables would fill up the .cvs file if storing a large amount of information which would lead to performance drop anyway
Large amounts of variables really aren't a big of a deal as people think. One person says they are then everyone goes on says that to anyone who asks from now on. I know servers with literally over 250k variables that have no issues with performance because of variables.
Of course variables will be faster in any case, but it is SO MUCH easier to modify yaml files than varables..
@aescraft - Yes you can but I wanted a configuration, where most of the things can be set changed from a notepad++. Not from in-game.
Just enable effect commands then you can just do things like
code_language.skript:
!set {whatever} to "whatever"
from in game or console. If you're talking about user facing settings, its easy to use a system that is actually well implemented like skript-json (which i'll give you it isn't as easy to edit a json file than a yaml file) or just make your own system for configurations if you can't use skript options for some reason...
 
Interesting topic, I never knew YAMLs had to go through crazy shit JUST to find one value. I avoided using YAMLs for player data because I knew how inefficient that would be anyways. But, the fact that it takes more than quintuple the time to retrieve yaml values than variables is just embarrassing. Is there a possible way devs can make YAMLs more efficient in targeting single values?
 
Interesting topic, I never knew YAMLs had to go through crazy shit JUST to find one value. I avoided using YAMLs for player data because I knew how inefficient that would be anyways. But, the fact that it takes more than quintuple the time to retrieve yaml values than variables is just embarrassing. Is there a possible way devs can make YAMLs more efficient in targeting single values?
The OP covers this... a perfect system would be like skript-json where the file is parsed once then you can reference it like {_yaml::value::value}
 
Status
Not open for further replies.