How to debug

Debugging is essential in problem-solving. Every programmer must know how to debug.

In this short tutorial, I'll demonstrate ways of debugging your problems in skript



Debugging
[[ Introduction ]]

Debugging is essentially checking if everything works correctly
for instance, A -> B -> C... -> Z
and if A doesn't show Z, it means that anything could have gone wrong

This tutorial covers console debugging as well as other good practices

[[ Explicitly showing the problem ]]

A bad code here looks like this - source: skUnity Discord

format gui slot 24 of player with glowing iron sword named "&6{@flower} &eKitPVP &6{@flower}" with lore "&c&lKitPVP is currently under development!", "&f", "&7Kill other players to", "&7gain exp and level up!", "&f", "&e{@flower} &fKits", "&e{@flower} &fPvP Levels", "&e{@flower} &fLeaderboards" "&f", "&fVersions &b1.8.x - 1.15.x", "&b%{_players_kitpvp}% players online!", "&aClick here to connect!" to close then run player command "/kitpvp"


And the person says that this is not working because of named ""
If someone were to debug this, it would take ages. So? you should explicitly show what is wrong


format gui slot 24 of player with glowing iron sword named "test"


Then, you can see that it works
So lets test more things


format gui slot 24 of player with glowing iron sword named "test" with lore "hi"


it also works

then you start making it longer and longer until you realize

"&e{@flower} &fLeaderboards" "&f", "&fVersions &b1

you forgot to add a comma there


[[ Condition checking / Debug messages]]
Your problems most occur when you least expect it. So it is a good idea to check if everything else is working

NOTE: DO NOT USE MESSAGE/SEND INSTEAD OF BROADCAST
here is why:


send effects are often unreliable
here is an example

on right click:
send "fired"


this debug seems to work until you right click on a player
this is because send doesn't always send to the person who is activating the event

there are more examples where send might not work

on projectile hit:
send "hit"


when you are debugging, you need to make sure that everything besides the things you are testing is working properly

also, broadcast sends the message to console as well. if you have really long debugs, you can scroll up on your console whereas default minecraft clients have that scroll limit

please use broadcasts over sends/messages


A simple example is a person arguing that 'on mine' doesn't work


on mine:
if {mine} is 1:
give 1 diamond to player



If we want to be sure that 'on mine' doesn't work, we can add debug messages


on mine:
broadcast "on mine called"
broadcast "{mine} is %{mine}%"
if {mine} is 1:
broadcast "{mine} = 1 condition passed"
give 1 diamond to player


when they ran the code, it broadcasted

on mine called
{mine} is <none>

that means, {mine} was not set at the first place


For example, let's look at a bad code posted on the skunity discord

every 1 second:
loop all players:
if {kit.%loop-player%::dwarf} is 1:
if loop-player is sneaking:
add 1 to {dwarf.timer::%loop-player%}
if loop-player is not sneaking:
remove 2 from {dwarf.timer::%loop-player%}
if {dwarf.timer::%loop-player%} is less than 1:
set {dwarf.timer::%loop-player%} to 0


As you can see, there can be multiple things going wrong
  • is {kit.%loop-player%::dwarf} not set?
  • is loop-player is sneaking condition working?
  • is adding 1 to {dwarf.timer::%loop-player%} working?

  • ...


So, here is a code with debug messages

every 1 second:
loop all players:
broadcast "looping player %loop-player%"
if {kit.%loop-player%::dwarf} is 1:
broadcast "dwarf is 1"
if loop-player is sneaking:
add 1 to {dwarf.timer::%loop-player%}
broadcast "%{dwarf.timer::%loop-player%}%"
if loop-player is not sneaking:
broadcast "happening"
remove 2 from {dwarf.timer::%loop-player%}
broadcast "%{dwarf.timer::%loop-player%}%"
if {dwarf.timer::%loop-player%} is less than 1:
set {dwarf.timer::%loop-player%} to 0
broadcast "stopped"


They should use lists; other than that,
when they ran the code, it broadcasted
looping player notch

so that means, {kit.%loop-player%::dwarf} is not set


[[ Step checking ]]
The use of functions allows us to embed multiple actions into one line. Because of this, they are prone to bugs

For example

set {_elo} to {_k} * (1 - 1 / (1 + (10^(-1 * (({_a} - {_b}) / {_n}))))))


There are a number of things that can go wrong here
So, split the thing into multiple parts


set {_awin} to {_a} - {_b}
set {_aexponent} to -1 * ({_awin} / {_n})
set {_aexpected} to 1 / (1 + (10^{_aexponent}))
return {_k} * (1 - {_aexpected})


Add broadcast as much as you like

[[ Examples ]]
Examples are a good way of debugging things

For example
Let's say you want to use metadata but the code is not working

if metadata {_str} or (substring of {_wtf} from {_a} to 10) of {_p}= "{@optionsomething}":


Start from the examples.

If the example was

metadata "lastchat" of player


Then check if the below works in the first place

command /debug:
trigger:
set {_metadata} to metadata ("lastchat" or "2ndlastchat") of player
send "%{_metadata}%"


And, without a doubt, that code doesn't work.
That means you cant just use 2 values on metadata.
That also means you cant read syntax :emoji_frowning:


Debugging Tools
Debugging is tedious, debugging is hard. And programmers want to automate such stuff

[

    I created a debugging tool for lists.
    It helps you inspect the layers of a list with ease

    Requires Skript 2.4+

    function List_Print(node: string, layer: string=" ", list: string="", currentlayer: string="") :: strings:
    # Prints a list
    # example
    # set {listname::*} to online players
    # send List_Print("listname")
    #
    # do not touch list and current layer
    # layer is customizeable and is defaulted to 4 spaces
    set {_returnarr::1} to "%{_currentlayer}%[%{_node}%]"
    set {_t} to 1
    loop indices of {%{_list}%%{_node}%::*}:
    add 1 to {_t}
    set {_returnarr::%{_t}%} to "%{_currentlayer}%%{_layer}%%loop-value%:%{%{_list}%%{_node}%::%loop-value%}%" if {%{_list}%%{_node}%::%loop-value%} is set
    if {%{_list}%%{_node}%::%loop-value%::*} is set:
    loop List_Print(loop-value, {_layer}, "%{_list}%%{_node}%::", "%{_currentlayer}%%{_layer}%"):
    add 1 to {_t}
    set {_returnarr::%{_t}%} to loop-value-2
    return {_returnarr::*}



    example response
    executing 'broadcast List_Print("island")'
    [island]
    [farm]
    [admin]
    069a79f4-44e9-4726-a5be-fca90e38aaf5:Notch
    b22b0d29-ce73-4b98-8aad-f6a7aa47c1af:lilMelon
    e2016a5c-f93b-4e60-984d-d794ce853d03:Nopeful
    [config]
    [permname]
    0:banned
    1:visitor
    2:member
    3: owner
    4:admin
    [size]
    1:2
    2:4
    3:6
    [id]
    [-1]
    [setting]
    [player]
    build:4
    [0]
    [setting]
    spawnpoint:x: -12, y: 128, z: -12
    [1]
    reference:0
    [4]
    level:1
    owner:nopeless
    reference:5
    [statistic]
    createdtime:1/2/20 6:2 PM
    lastactive:1/2/20 6:2 PM
    [5]
    [children]
    4:true
    8:true
    level:1
    owner:nopeless
    [setting]
    [permission]
    df5f766e-8d62-42af-9f10-18f95e671067:3
    [player]
    ban:3
    build:2
    chest:2
    damageentity:2
    drop:2
    enter:1
    entityinteract:2
    item:2
    kick:2
    nofalldamage:1
    nofiredamage:1
    pvp:3
    redstone:2
    trample:5
    [statistic]
    createdtime:1/2/20 6:2 PM
    lastactive:1/2/20 6:2 PM
    [8]
    level:1
    owner:nopeless
    reference:5
    [statistic]
    createdtime:1/2/20 6:2 PM
    lastactive:1/2/20 6:2 PM
    [9]
    level:1
    owner:nopeless
    [setting]
    [permission]
    df5f766e-8d62-42af-9f10-18f95e671067:3
    [player]
    ban:3
    build:2
    chest:2
    damageentity:2
    drop:2
    enter:1
    entityinteract:2
    item:2
    kick:2
    nofalldamage:1
    nofiredamage:1
    pvp:3
    redstone:2
    trample:5
    [statistic]
    createdtime:1/2/20 6:2 PM
    lastactive:1/2/20 6:2 PM
    [player]
    [df5f766e-8d62-42af-9f10-18f95e671067]
    [island]
    nopeless's farm - 1:5
    nopeless's farm - 2:8
    nopeless's farm - 3:9
    nopeless's farm:4
    maxisland:100


    [[ Create your own debugging tool ]]
    Sometimes, you might want to create a debugging tool yourself, here is an example

    command /debuginventory:
    # sends every inventory action
    trigger:
    if {debug::inventory} = true:
    set {debug::inventory} to false
    else:
    set {debug::inventory} to true
    send "set debuginventory to %{debug::inventory}%"



    on inventory click:
    if {debug::inventory} = true:
    send "inventory name:%inventory name of event-inventory%"
    send "event-item:%event-item%"
    send "event-inventoryaction:%event-inventoryaction%"
    send "event-inventory:%event-inventory%"
    set {_slot} to event-slot
    send "event-slot:%{_slot}%"
    send "event-clicktype:%event-clicktype%"
    send "hotkey:%event-key%"
    send "hotkey slot:%event-hotbar-slot%"
    send "cursor slot:%cursor slot of player%"
    send inventory name of event-inventory
    send "-slot:%index of event-slot%"
    send "-count:%item amount of {_slot}%"


    This is a debugging tool I use when scripting things related to GUI

    Tips & Tricks
    [[ Skript effects ]]

    Did you know that you can run Skript™ can run effects on the fly?

    go to
    Skript/config.sk
    turn on


    enable effect commands: false
    to
    enable effect commands: true

    allow ops to use effect commands: false
    to
    allow ops to use effect commands: true


    then, in a Minecraft server where you are op, try typing
    !send "hello world"
    it will say something like

    executing 'send "hello world"'
    hello world

    this feature allows you to run functions as well
    if you are wondering what {block::1} is, just type
    !send "%{block::1}%"
    without the hassle of going to scripts and putting a command debug for that :emoji_slight_smile:

    more detail can be found here
    https://docs.skunity.com/guides/guide/run-effect/

    [[ Debug option ]]
    Tired of deleting broadcast every time you fixed the problem only to type it again?
    There is an easy way to toggle broadcast on these

    options:
    debug:broadcast

    on mine:
    {@debug} "on mine called"
    # stuff


    to turn it off,

    options:
    debug:set {_faioeal} to
    # remember to set it to something that has a 0 possibility of creating conflicts
    on mine:
    {@debug} "on mine called"
    # stuff


    [[ Debug broadcast works but code doesn't ]]

    a code from skUnity Discord

    function survivalscoreboard(p: player):
    send "true" to {_p}
    <some effect> "World = %{_p} <some expression>%" to {_p}


    The debug "true" gets sent but the line below doesn't.
    The person also tested the code outside the function and said it executed the second line

    This means one of 2 things are happening
    1. the second line expression bugged out for some reason
    • a) this is possible if addon developers didn't fully consider or put exception handling on their code. => Check console
    • b) this is possible if the expression doesn't work on functions. => clearly show the code not working, issue a bug report on their respective repositories.

    2. the second line effect broke for some reason
    • a) this is possible on some functions, where addon developers didn't fully consider the use of effects inside functions. => try changing parameters to a fixed global variable. if that works, submit a bug issue on git.
    • b) the effect doesn't work in those particular parameters inside the function => change all parameters into global variables and then test it inside and outside a function
      - i) if the global variable version doesn't work, it means that the object has a problem referencing/serializing the data. => submit a bug on git
      - ii) if the global variable works, test if the parameter version is the same. for example
      # is the object same
      command objtest:
      trigger:
      set {global} to <something>
      if {global} = param({global}):
      send "no problems"
      else:
      send "something went wrong"

      function param(o: object) :: object:
      return {_o}





    [[ Flawless Debug ]]
    If you are checking a string, don't broadcast the string only
    Bad example

    set {_str} to substring of "whatever" from {_a} to {_b}
    broadcast {_str}


    Good example

    set {_str} to substring of "whatever" from {_a} to {_b}
    broadcast "{_str} = %{_str}%"


    This is because if {_str} is not set(<none>), skript will not broadcast at all, which might confuse you a lot.
    Generally a good practice to add more info when debugging


    [[ Recursive things ]]
    Using recursive features? Good! you are an above-average programmer. However, you are also putting yourself at a high risk of constantly crashing your server.
    Put delays and emergency stops on those functions

    Bad example

    function factorial(n: number) :: number:
    return 1 if {_n} = 1
    return {_n} * factorial({_n} - 1)


    Good example

    function factorial(n: number) :: number:
    stop if {emergencystop} = 1
    wait 1 tick
    return 1 if {_n} = 1
    return {_n} * factorial({_n} - 1)


    If you are so confident that it will never break, you can safely remove those delays and stops
    However, it is crucial when developing


    [[ Types ]]
    Skript variables have types. strings, integers(longs), booleans, etc. But there seems to be virtually no way to test the type of the variable, right?

    Rezz created a snippet that allows you to inspect the type of a variable. https://forums.skunity.com/threads/rezzs-snippets-things-you-can-do-in-pure-skript-2-2.2471/ <- original link. All credits to Rezz

    options:

    DATA: skript-types

    function declareUsableType(addon: text, user-input: text):

    set {_type} to {_user-input} parsed as type

    {_type} is set

    add {_type} to {{@DATA}::types::*}
    set {_index} to amount of {{@DATA}::types::*}

    set {{@DATA}::type-of::%{_user-input}%} to {_type}
    set {{@DATA}::user-input-of::%{_type}%} to {_user-input}
    set {{@DATA}::index-of::%{_type}%} to {_index}

    add {_index} to {{@DATA}::addons::%{_addon}%::types::*}

    if {{@DATA}::addons::%{_addon}%} isn't set:

    set {{@DATA}::addons::%{_addon}%} to {_addon}

    function declareUsableTypes(addon: text, types: texts):

    loop {_types::*}:
    declareUsableType({_addon}, loop-value)

    on script load:

    delete {{@DATA}::*}

    set {{@DATA}::object-type} to "object" parsed as type

    # Data Types
    declareUsableTypes("skript", ("type", "boolean", "integer", "number", and "text"))

    # Players & Entities
    declareUsableTypes("skript", ("player", "offline player", "command sender", "living entity", "entity", and "projectile"))

    # Blocks & Locations
    declareUsableTypes("skript", ("block", "location", "vector", and "direction"))

    # Worlds
    declareUsableTypes("skript", ("biome", "chunk", "world", and "weather"))

    # Items & Inventories
    declareUsableTypes("skript", ("item", "item type", "potion effect", "inventory", "slot", "inventory action", and "click action"))

    # Enchantments
    declareUsableTypes("skript", ("enchantment", and "enchantment type"))

    # Time
    declareUsableTypes("skript", ("time", "timespan", "timeperiod", and "date"))

    # Misc
    declareUsableTypes("skript", ("gamemode", "damage cause", "color", "tree type", and "particle effect"))

    on script unload:

    delete {{@DATA}::*}

    function typeof(var: object) :: type:

    loop {{@DATA}::types::*}:
    if {_var} is loop-value:
    return loop-value
    return {{@DATA}::object-type}


    typeof(<variable>) returns type
    - Returns the variable's type, or 'object' if its type isn't declared.
    declareUsableTypes(<addon name>, <list of types>)
    - Declare all types listed in their user-input form. This function simply calls declareUsableType() on each list item.

    declareUsableType(<addon name>, <type in standard user-input form>)
    - Declare a usable type. It's important to declare specific types before general types because declaration order is preserved. i.e. A variable of a specific type, like 'player', can also be considered a more general type as well -- 'offline player' in this example. If 'offline player' is declared before 'player', 'player' type variables will always be considered 'offline players'.

    You can check more details and examples on the link

    [[ Console ]]
    If your code creates errors on console, believe it or not, it's actually a good sign. You can find the problem of your code by reading the error logs

    Here are some common bugs and how to fix them
    • NullPointer
      - this usually happens when the variable you put inside the function as an argument is not set. Also, see array out of bounds exception
    • OutOfBounds
      - this happens when you put 2 values in 1 argument when you are only allowed to put 1. This is not your fault. Skript tries to create lists whenever they see a comma. To fix this problem, add parentheses. ex) func(1, 2, 3) -> func((1), (2), (3))
    • TypeError
      - this happens when you have the incorrect type. Some of them are unintuitive. If you get "expected integer but number", add round({_thevariable}) to it. Some other TypeErrors are hard to fix but if you encounter them, please shorten your code so the error can be explicit

    • IllegalArgument
      - you entered an argument that is out of the range of that function. For example, you cant set slot 100 of an inventory
    • StackOverflow
      this is a rare error. but most fall into 2 categories
      - lack of ram: simply give your server more ram
      - recursive function: there is a recursive function in your code that isn't working properly.

    Common mistakes
    just some common mistakes from people

    [[ Color ]]
    - bold, italics don't show -> color codes reset formatting. if you want do disable it, go to the skript config
    - color does not change to Minecraft color doe -> use colored expression
    [

      - lists don't work properly -> lists are probably not what you think. read above and get listprint
      - list keeps deleting after restart -> are you sure you don't have a "clear {list::*}" somewhere
      - list is not ordered -> list's indices are not what you think. read above and get listprint
      - adding stuff to lists is slow -> adding is slow. use set and loops instead
      [[ Addons ]]
      - using skquery, wild skript, umbaska, skrayfall -> just no
      - using skript-mirror for simple stuff -> why
      idk ill add more if i see more

      Addition to things
      Something is wrong? Want to contribute to this page? PM me on this account with details, I will update it.



      Update log
      - 2020/1/23 : Created page
      - 2020/2/5 : added [ Debug sends but code doesn't work ]
      - 2020/2/25 : edited format so it looks better
      - 2020/2/29 : added skript effects tutorial
      - 2020/3/1 : added more information
      - 2020/5/30 : added common mistakes section