Skript Player Data System

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

ShaneBee

Supporter +
Addon Developer
Sep 7, 2017
2,247
241
73
Vancouver, Canada
One of the main performance issues with Skript is the variable system.
If used correctly it can be great, but it is often abused/overused which can cause major performance issues with a server.

Let's do some basic math:
Let's say per-player you have the following variables:
- Money
- Points
- Homes (5)
- A player-vault with 27 slots
- Nickname
- team/group/clan
This alone is 36 variables.... per player. (Most servers have a lot more than this, this is just a small example)
Now let's say over a short period of time, you've had 1000 unique joins.
That is 36,000 variables saved in one file.
Each time your server loads, it loads ALL of these variables into RAM. Most of these players won't ever come back but you've got a lot of variables loading each time.

Things like this can cause great stress on your server. So what can we do to alleviate that pressure on your server? Well, this is where I introduce to you, a PlayerData system using skript-yaml.

This system is quite easy to use, and is very flexible. The goal here is to create a PlayerData file per player, and only load into RAM the players that are online.

So let's get started.
This system uses skript-yaml, if you need more info check out the SKRIPT-YAML REPO for more information.

Loading PlayerData into RAM:
When the player joins, we will load their data into RAM for easy/quick access.
As stated, this allows us to load into RAM the data for players which are currently online, and not the 100s or 1000s of players who won't ever come back.
We will also set their current name and last-login for future use
code_language.skript:
on join:
    load yaml "plugins/MyData/%uuid of player%.yml" as "data-%uuid of player%"
    set yaml value "name" in "data-%uuid of player%" to name of player
    set yaml value "last-login" in "data-%uuid of player%" to now
    save yaml "data-%uuid of player%"

Unloading PlayerData:
When the player quits, we will save/unload their data.
This again will help keep our RAM at efficient usage.
code_language.skript:
on quit:
    save yaml "data-%uuid of player%"
    unload yaml "data-%uuid of player%"

Saving PlayerData periodically to file:
Rather than saving PlayerData to file every time we make a change to it, we will do it periodically.
Why you ask? Thing of systems like jobs, where players get payed money everytime they do an action, like for example mining. Now imaging 100 players online, mining hundreds of blocks a second. We don't want to constantly be writing to file, this would put a lot of strain on the server and affect performance.
So we periodically save to file.
Don't worry, the PlayerData will always be in ram, you can write to/read from without affecting performance. The save we are talking about here is writing to your yaml file.
code_language.skript:
# Every 5 minutes we will save all current online player data just to be safe
# Adjust time to your liking
every 5 minutes:
    loop all loaded yaml:
        save loop-value
        # We add a small wait between each player data to prevent
        # a massive amount of lag if a lot of players are online
        # Adjust to suit your server's needs
        wait 2 ticks

Accessing/Modifying our PlayerData:
FUNCTIONS
We will create some functions for quick access to player data
I'm using money and points as an example, but you can use whatever data you need for your server.
These simple functions allow you to easily get/set/add/remove info from PlayerData files.
code_language.skript:
# Money
function getMoney(p: player) :: number:
    return yaml value "money" from "data-%uuid of {_p}%"

function setMoney(p: player, n: number):
    set yaml value "money" in "data-%uuid of {_p}%" to {_n}

function addMoney(p: player, n: number):
    set {_money} to yaml value "money" in "data-%uuid of {_p}%"
    add {_n} to {_money}
    set yaml value "money" in "data-%uuid of {_p}%" to {_money}

function removeMoney(p: player, n: number):
    set {_money} to yaml value "money" in "data-%uuid of {_p}%"
    remove {_n} from {_money}
    set yaml value "money" in "data-%uuid of {_p}%" to {_money}

# Points
function getPoints(p: player) :: number:
    return yaml value "points" from "data-%uuid of {_p}%"

function setPoints(p: player, n: number):
    set yaml value "points" in "data-%uuid of {_p}%" to {_n}

function addPoints(p: player, n: number):
    set {_points} to yaml value "points" in "data-%uuid of {_p}%"
    add {_n} to {_points}
    set yaml value "points" in "data-%uuid of {_p}%" to {_points}

function removePoints(p: player, n: number):
    set {_points} to yaml value "points" in "data-%uuid of {_p}%"
    remove {_n} from {_points}
    set yaml value "points" in "data-%uuid of {_p}%" to {_points}

Usage:
So how do we use these functions? Well I'm glad you asked, if you haven't used functions before I recommend checking out the FUNCTIONS WIKI for more info. If you have used functions before you will know how extremely easy they are to use.

Here is an example command using our newly created functions:
code_language.skript:
# Now that we have our base set up, lets use our data
# Quick example of a points command using our data functions
# In this example i will be skipping some obvious conditions for args
# I just want to show the basic usage of the functions
# I will also not be putting obvious messages, again, just to simply show how this works

command /points <text> [<player=%player%>] [<number>]:
    trigger:
        if arg-1 = "get":
            # here we can simply print a message with the points of the player/arg
            send "Points for %arg-2%: %getPoints(arg-2)%"
        else if arg-1 = "set":
            # here we can set points
            setPoints(arg-2, arg-3)
        else if arg-1 = "add":
            # Here we can add to the points
            addPoints(arg-2, arg-3)
        else if arg-1 = "remove":
            # Here we can remove from the points
            removePoints(arg-2, arg-3)

Here is a sample event using our newly created functions:
code_language.skript:
# We're going to use on join, to set the defaults for the player
# If the values are not currently set, let's set them

on join:
    # Here we set the player's points to 10 when they join if its not currently set
    if getPoints(player) is not set:
        setPoints(player, 10)
    # Here we set the player's money to 500 when they join if its not currently set
    if getMoney(player) is not set:
        setMoney(player, 500.00)

Clearing old PlayerData:
Lastly, lets say we want to clear out old player data (player's who havent logged in for a while)
We can simply run a command to clear out old data
Obviously this command will need permission checks and stuff, again, this is just an example

Special Note: A command like this should not be run when a lot of players are online
code_language.skript:
command /clearOldData:
    trigger:
        set {_n} to 0 # lets create a counter
        loop all offline players:
            # here we are going to load each offline player's data, and check the difference between now and last-login
            # if grater than 100 days, we will delete their data
            # else we will just unload it and move onto the next
            load yaml "plugins/MyData/%uuid of loop-offline player%.yml" as "off-%uuid of loop-offline player%"
            set {_t} to yaml value "last-login" from "off-%uuid of loop-offline player%"
            if difference between now and {_t} > 100 days:
                delete yaml "off-%uuid of loop-offline player%"
                # lets just let the console know we're deleting this
                send "Deleting data for %loop-offline player%" to console
                # We will add 1 to {_n} so we know how many player data's we cleared
                add 1 to {_n}
            else:
                unload yaml "off-%uuid of loop-offline player%"
            wait 1 tick
        # Now we tell the console how many data's we cleared
        send "Removed player data for %{_n}% inactive player(s)" to console

Example PlayerData file:
Here is an example of what MY PlayerData file looks like, using this code:
YAML:
name: ShaneBee

last-login: !skriptdate '2020-03-23T13:59:42.570-07:00'

points: 10

money: 500.0
 
Last edited:
One of the main performance issues with Skript is the variable system.
If used correctly it can be great, but it is often abused/overused which can can major performance issues with a server.

Let's do some basic math:
Let's say per-player you have the following variables:
- Money
- Points
- Homes (5)
- A player-vault with 27 slots
- Nickname
- team/group/clan
This alone is 36 variables.... per player. (Most servers have a lot more than this, this is just a small example)
Now let's say over a short period of time, you've had 1000 unique joins.
That is 36,000 variables saved in one file.
Each time your server loads, it loads ALL of these variables into RAM. Most of these players won't ever come back but you've got a lot of variables loading each time.

Things like this can cause great stress on your server. So what can we do to alleviate that pressure on your server? Well, this is where I introduce to you, a PlayerData system using skript-yaml.

This system is quite easy to use, and is very flexible. The goal here is to create a PlayerData file per player, and only load into RAM the players that are online.

So let's get started.
This system uses skript-yaml, if you need more info check out the SKRIPT-YAML REPO for more information.

Loading PlayerData into RAM:
When the player joins, we will load their data into RAM for easy/quick access.
As stated, this allows us online to load into RAM the player's which are currently online, and not the 100s or 1000s of players who won't ever come back.
We will also set their current name and last-login for future use
code_language.skript:
on join:
    load yaml "plugins/MyData/%uuid of player%.yml" as "data-%uuid of player%"
    set yaml value "name" in "data-%uuid of player%" to name of player
    set yaml value "last-login" in "data-%uuid of player%" to now
    save yaml "data-%uuid of player%"

Unloading PlayerData:
When the player quits, we will save/unload their data.
This again will help keep our RAM at efficient usage.
code_language.skript:
on quit:
    save yaml "data-%uuid of player%"
    unload yaml "data-%uuid of player%"

Saving PlayerData periodically to file:
Rather than saving PlayerData to file every time we make a change to it, we will do it periodically.
Why you ask? This of systems like jobs, where players get payed money everytime they do an action, like for example mining. Now imaging 100 players online, mining hundreds of blocks a second. We don't want to constantly be writing to file, this would put a lot of strain on the server and affect performance.
So we periodically save to file.
Don't worry, the PlayerData will always be in ram, you can write to/read from without affecting performance. The save we are talking about here is writing to your yaml file.
code_language.skript:
# Every 5 minutes we will save all current online player data just to be safe
# Adjust time to your liking
every 5 minutes:
    loop all loaded yaml:
        save loop-value
        # We add a small wait between each player data to prevent
        # a massive amount of lag if a lot of players are online
        # Adjust to suit your server's needs
        wait 2 ticks

Accessing/Modifying our PlayerData:
FUNCTIONS
We will create some functions for quick access to player data
I'm using money and points as an example, but you can use whatever data you need for your server.
These simple functions allow you to easily get/set/add/remove info from PlayerData files.
code_language.skript:
# Money
function getMoney(p: player) :: number:
    return yaml value "money" from "data-%uuid of {_p}%"

function setMoney(p: player, n: number):
    set yaml value "money" in "data-%uuid of {_p}%" to {_n}

function addMoney(p: player, n: number):
    set {_money} to yaml value "money" in "data-%uuid of {_p}%"
    add {_n} to {_money}
    set yaml value "money" in "data-%uuid of {_p}%" to {_money}

function removeMoney(p: player, n: number):
    set {_money} to yaml value "money" in "data-%uuid of {_p}%"
    remove {_n} from {_money}
    set yaml value "money" in "data-%uuid of {_p}%" to {_money}

# Points
function getPoints(p: player) :: number:
    return yaml value "points" from "data-%uuid of {_p}%"

function setPoints(p: player, n: number):
    set yaml value "points" in "data-%uuid of {_p}%" to {_n}

function addPoints(p: player, n: number):
    set {_points} to yaml value "points" in "data-%uuid of {_p}%"
    add {_n} to {_points}
    set yaml value "points" in "data-%uuid of {_p}%" to {_points}

function removePoints(p: player, n: number):
    set {_points} to yaml value "points" in "data-%uuid of {_p}%"
    remove {_n} from {_points}
    set yaml value "points" in "data-%uuid of {_p}%" to {_points}

Usage:
So how do we use these functions? Well I'm glad you asked, if you haven't used functions before I recommend checking out the FUNCTIONS WIKI for more info. If you have used functions before you will know how extremely easy they are to use.

Here is an example command using our newly created functions:
code_language.skript:
# Now that we have our base set up, lets use our data
# Quick example of a points command using our data functions
# In this example i will be skipping some obvious conditions for args
# I just want to show the basic usage of the functions
# I will also not be putting obvious messages, again, just to simply show how this works

command /points <text> [<player=%player%>] [<number>]:
    trigger:
        if arg-1 = "get":
            # here we can simply print a message with the points of the player/arg
            send "Points for %arg-2%: %getPoints(arg-2)%"
        else if arg-1 = "set":
            # here we can set points
            setPoints(arg-2, arg-3)
        else if arg-1 = "add":
            # Here we can add to the points
            addPoints(arg-2, arg-3)
        else if arg-1 = "remove":
            # Here we can remove from the points
            removePoints(arg-2, arg-3)

Here is a sample event using our newly created functions:
code_language.skript:
# We're going to use on join, to set the defaults for the player
# If the values are not currently set, let's set them

on join:
    # Here we set the player's points to 10 when they join if its not currently set
    if getPoints(player) is not set:
        setPoints(player, 10)
    # Here we set the player's money to 500 when they join if its not currently set
    if getMoney(player) is not set:
        setMoney(player, 500.00)

Clearing old PlayerData:
Lastly, lets say we want to clear out old player data (player's who havent logged in for a while)
We can simply run a command to clear out old data
Obviously this command will need permission checks and stuff, again, this is just an example

Special Note: A command like this should not be run when a lot of players are online
code_language.skript:
command /clearOldData:
    trigger:
        set {_n} to 0 # lets create a counter
        loop all offline players:
            # here we are going to load each offline player's data, and check the difference between now and last-login
            # if grater than 100 days, we will delete their data
            # else we will just unload it and move onto the next
            load yaml "plugins/MyData/%uuid of loop-offline player%.yml" as "off-%uuid of loop-offline player%"
            set {_t} to yaml value "last-login" from "off-%uuid of loop-offline player%"
            if difference between now and {_t} > 100 days:
                delete yaml "off-%uuid of loop-offline player%"
                # lets just let the console know we're deleting this
                send "Deleting data for %loop-offline player%" to console
                # We will add 1 to {_n} so we know how many player data's we cleared
                add 1 to {_n}
            else:
                unload yaml "off-%uuid of loop-offline player%"
            wait 1 tick
        # Now we tell the console how many data's we cleared
        send "Removed player data for %{_n}% inactive player(s)" to console

Example PlayerData file:
Here is an example of what MY PlayerData file looks like, using this code:
YAML:
name: ShaneBee

last-login: !skriptdate '2020-03-23T13:59:42.570-07:00'

points: 10

money: 500.0
This is fantastic. Thank you so much for this!

Just one question though, how would we go about creating an efficient toplist using skript-yaml? Since things like mirror-util's sorting function use list variables for sorting. What would be the most efficient way of pulling all the data from the yaml of all players and sorting it be?
 
Last edited:
This is fantastic. Thank you so much for this!

Just one question though, how would we go about creating an efficient toplist using skript-yaml? Since things like mirror-util's sorting function use list variables for sorting. What would be the most efficient way of pulling all the data from the yaml of all players and sorting it be?
I recommend creating a new thread for a question like that, as it doesn't really fit the theme of this thread.
 
Could you provide some pictures of how your directories look?
e.g:
upload_2020-6-21_20-35-38.png


I currently use Skutilities, but i want to switch - is it worth or would the performance be the same?
 
Could you provide some pictures of how your directories look?
e.g:
View attachment 4874

I currently use Skutilities, but i want to switch - is it worth or would the performance be the same?
No real reason to show an image of the directory, it would look about the same.
As for skUtilities, that addon died years ago, and the performance of skUtil vs Skript-yaml is horrible. Skript-yaml is the way to go.
 
  • Like
Reactions: Laukage