Advanced Skript Tutorial

Hello and welcome to this tutorial!

You've taken a look at Skript, you've made some cool commands, and you generally know what you're doing (if you don't, check this out!), and you're ready to upgrade your scripting game.
In this tutorial, I'll be showing you some advanced Skript tactics, code efficiency, and some more useful stuff that you might not have known.

Chapter 1 - Changers

Most expressions can be changed, and you probably recognize these changers: set, add, remove, delete and clear. They're used for changing expressions, such as
add 1 to {list::*}

But what if I told you some are faster than others? You probably wouldn't believe me, but I'm not lying. Currently, the 'set' changer is faster than 'add' and 'remove'. So code like this,
add 1 to {_list::*}

could be made more efficient by replacing it with e.g.
set {_list::1} to 1

This has been proven by a test (made my @KingAlterIV) which contained the following code:
command /variables:
        delete {test::*} # resets the values
        set {_n} to now
        loop 10000 times:
            add loop-number to {test::*} # here the 'add' changer is used
        delete {test::*} # resets the values
        set {_now} to difference between {_n} and now
        set {_n2} to now
        loop 10000 times:
            set {test::%loop-number%} to loop-number # here the 'set' changer is used
        set {_now2} to difference between {_n2} and now
        message ""
        message "&e&l10000 &rvariables adding: %{_now}%"
        message "&a&l10000 &rvariables setting: %{_now2}%"
        message ""
        delete {test::*}

The results were pretty conclusive:
10000 variables adding: 55.08 seconds
10000 variables setting: 5.01 seconds

The 'set' changer was in this test a whapping 1099.4% faster.
In conclusion: you should use the 'set' changer above all changers (except delete and clear)

Chapter 2 - Loops

Loops are being used all the time. They're a great way for sorting information 1 by 1, and you've probably used them before. You might think the most optimal loops look something like this:

loop all players:
    if player is in world "Lobby":
        send "Like this tutorial!!" to loop-player

set {_loop index} to 0
loop {players::*}:
    set slot {_loop index} of player's current inventory to loop-value
    add 1 to {_loop index}

But the most efficient code for these 2 examples actually looks something like this:
send "Like this tutorial!!" to all players in world "Lobby"

loop {players::*}:
    set slot (loop-index parsed as int - 1) of player's current inventory to loop-value

Most effects accept multiple players, and that's why you can use 'all players..'
Some shortcuts:

The expression loop-index is the index where the loop currently is, e.g.:

set {_index} to 1
loop {values::*}:
    # some code..
    set {_index} to {_index} + 1

This code can be simplified by just using
loop {values::*}:
    # some code

Here you can replace the variable {_index} with loop-index, so you don't have to create another variable.

If you want indices to start, for example, at 0 (maybe for GUIs) you can achieve this with some simple math:

loop ..:
    broadcast "%loop-index parsed as int - 1%"

Being equal to
set {_index} to 0
loop ..:
    broadcast "%{_index}%"
    set {_index} to {_index} + 1

All players
This expression is basically just all players. You can use this expression instead of looping all players, for example:

loop all players in world "world":
    teleport loop-player to {my location}

could be
teleport all players in world "world" to {my location}

Every expression in vanilla Skript (the default syntax in Skript without any addons) currently supports multiple players, so you can always use it.

Chapter 3 - Packets

Packets.. for most people one of the hardest things to understand in Skript. But it's actually easier than you thought, thanks to some websites. Here, I'll be showing you how to use the protocol wiki from

You'll want to use MundoSK for packets, this addon currently supports packets the best.

For this tutorial I'll be showing you how to modify the basics, and I'm gonna be using the packet below.

As you can see, the packet above will spawn an experience orb only for specific players.
You can see that it's a client packet ('Bound To' section) and to the right you can see the fields you will need to change. The Entity ID, the location, and the count. In the 'Field Type' section you can see what things you need to modify.

These 'Field Types' will make your life easier, since they literally tell you what things you need to change.
The current changers are these:
%type% pinfo %number% of %packet%
%type% array pinfo %number% of %packet%
%string% pinfo %number% of %packet%
%world% pentity %number% of %packet%
byte pnum %number% of %packet%
short pnum %number% of %packet%
int pnum %number% of %packet%
long pnum %number% of %packet%
float pnum %number% of %packet%
double pnum %number% of %packet%
byte array pnum %number% of %packet%
int array pnum %number% of %packet%
%string% pjson %number% of %packet%
%string% array pjson %number% of %packet%
%string% penum %number% of %packet%

If you look at the picture again, you see that the 'Entity ID' needs a 'VarInt' changer, which correlates to an integer, which you can change using
set int pnum %number% of %packet% to %integer%

If you haven't figured it out already, the 'Field Types' contain the changer. You can see that 'VarInt' contains 'int', so you'll have to use the expression
int pnum %number% of %packet%

The X, Y and Z fields are Doubles, so you will have to use
double pnum %number% of %packet%

And you can use this for every packet: you can look what expression you need to use that has the field type in it.

You may have figured out that in every syntax of modifying a packet, there's a %number% expression, and this is for keeping values apart. These count from 0, and you just have to follow the order the wiki lists them in.

So now I'll show you an example of the picture above with notes, to make it more clear.
command /I want an experience orb:
        # create a new packet, most of the times it looks like this: play_(client/server)_(packet name)
        set {_packet} to new play_server_spawn_entity_experience_orb
        # it's the first VarInt in the list, so use number 0
        set int pnum 0 of {_packet} to 1 # sets the Entity ID of the exp orb
        # this is the first Double in the list, so use number 0
        set double pnum 0 of {_packet} to player's x coordinate # sets the x coordinate of where the orb will be spawned to the player's x coordinate
        # this is the second Double in the list, so use 1
        set double pnum 1 of {_packet} to player's y coordinate + 1 # sets the y coordinate of the orb to player's y coord + 1
        # this is the third Double in the list, so use 2
        set double pnum 2 of {_packet} to player's z coordinate # sets the z coordinate of the orb to player's z coord
        # this is the first Short in the list, so use 0
        set short pnum 0 of {_packet} to 30 # spawn 30 exp orbs
        # now send the packet to the player
        send player packet {_packet}

For all packets names, you can take a look here for a list of up-to-date packets.
For more information take a look at this tutorial