1. Check out the Store if you're looking to Buy HL2RP or other schemas for your server. You can click Buy Schemas on the very top navigation bar to visit the store.
  2. Use the Plugin Center to easily subscribe to and auto-install Clockwork plugins to your server, or submit and share plugins you have developed.
  3. Having trouble setting up or developing with Clockwork? Check out the Wiki or post in the Support Forum for advice from fellow users.
Dismiss Notice
Hi Guest, you need a Steam account to register and post on these forums. Login with Steam at the top of the forums to get started!
Dismiss Notice
Hi Guest, do you want to buy HL2RP or another Clockwork schema? Visit the Cloud Sixteen Store to get started!

Other Clockwork Development Primer

Discussion in 'Development' started by Vortix, Feb 3, 2019.

  1. Vortix

    Vortix Legend Clockwork Customer Active Member

    Clockwork Development Primer
    Getting Started
    What is Lua?
    Lua is a scripting language, meaning that it is designed to be run by other programs. The most relevant example is the case of Garry's Mod: it is developed primarily in C++, but allows the use of Lua to expand upon its functionality. This gives game owners the ability to shape the game as they want to, within any restrictions placed on them by the game developers. It is the reason that GMod is what it is: whatever we want it to be.

    Like all things, Lua has updated itself over time. As of my writing this, its last major release was Lua 5.3. Garry's Mod uses a variation of Lua 5.1.

    Do I need anything to get started?
    You could certainly follow this thread just gathering information, but if you're just starting out it would be helpful for you to follow along. To begin with, I recommend visiting the Lua demo page here. It lets you write and read basic Lua code. Keep in mind, though, it uses Lua 5.3, but I will be sure to point out any differences in behaviour as we come across them.

    If you want to get into programming and haven't got one already, I'd recommend downloading a good text editor. Popular choices are Sublime Text, Atom, and Visual Studio Code. Sublime Text is an excellent lightweight editor. Atom provides a wealth of useful packages. I, personally, use Visual Studio Code. A good text editor can ease the programming experience tremendously.

    Lua
    Storing information

    We have variables in programming, just like mathematics, and we can define them similarly:
    Code:
    x = 2019
    Unlike in maths, however, it isn't a good idea to have single-letter variable names. Mathematicians are hindered by writing everything out by hand, and so long names can be very time consuming to write out. Additionally, mathematicians are often more interested in the properties of values, and what can be done with them, rather than what they're for. For programmers, however, each value has a specific purpose, which is very relevant to how we treat it. So, it is important that we can tell what each piece of information is supposed to represent. Let's re-make that variable:
    Code:
    currentYear = 2019
    That's better. Now, you may have noticed that I've joined the words together, and capitalised the first letter of the second one. Each language has restrictions on what can and can't be a variable name. Typically, it can't start with a number, and can only contain letters, numbers, and underscores. This ensures that the language can tell that you're referring to a variable. So, in a common convention known as lower camel case, words in variables are conjoined and the first letter of all words but the first are capitalised.

    We can even define multiple variables on one line:
    Code:
    currentYear, currentHour = 2019, 12
    This sets currentYear to 2019, and currentHour to 12. Personally, I prefer to declare each variable on its own line, but as you'll see later in this thread there are cases where using a single line is helpful.

    Displaying information
    I'll explain more of what's going on here later, but for now, know that surrounding a variable with parentheses and prefixing it with print will cause the program to output information. You can use this in conjunction with the variables I give you to see a result from executing the scripts. For example, the following will print out 2019 if placed after my currentYear definition:
    Code:
    print(currentYear)
    Additionally, Lua defines a variable called _VERSION, which stores some text stating the current version of Lua. We'll learn about storing text in the next section, but for now you can try printing out it out:
    Code:
    print(_VERSION)

    Basic data types
    What types of data can we store? We just saw how a number can be stored, but what if we want to store something else, like a word? And what other types of information might we come across in Lua 5.1?

    Let me introduce you to strings. A string is a collection of characters. To write a string in Lua, we write out the text we want to be in the string, and surround it with either double or single quotation marks. So, for example:
    Code:
    myForumName = "Vortix"
    myForumName = 'Vortix'
    
    Both lines of code shown above do the same thing, but if I start a string with one type of quotation, I must end it with the same kind. I can even put symbols in there:
    Code:
    myIntroduction = "Hi, I'm Vortix!"
    Careful with this, though: there are some symbols which need special treatment. For example, what if we want our string to contain a quotation mark? How will Lua know that I don't intend to end my string? In these cases, we prefix the character with a backslash to indicate that it should get special treatment:
    Code:
    myIntroduction = "Hello, I'm \"Vortix\"!"
    As can be seen above, we also have no problems using quotation marks of one kind within the other.

    There are other special cases, too. The backslash, for one! You need to prefix the backslash with a backslash if you want to make it clear that it should be treated as part of the string. You can use \t to get a tab character, and \n to get a newline character. There are many other cases, but I won't go through them all: if you're interested, you can check them out here. We call the process of applying the backslash escaping.

    There is one more way to write a string: through the use of double square brackets. Strings set up like this can span many lines, and don't take any characters to be special (except the ending sequence, ]]). So, you can write whatever characters you want in there without needing to escape them:
    Code:
    myIntroduction = [[
        "!'\
    ]]
    The next type of data to know about is the boolean. There are only two possibilities for a boolean: true, or false. This type is used to drive logical decisions. Have a look:
    Code:
    iAmVortix = true
    iAmNotVortix = false
    
    The last type of data to know about at this point is nil. The nil data type's only value is nil: it is used as a step-in value which means "no value is assigned to this". So, all variables are nil before they are set. If we want to, we can even set already-set variables to nil:
    Code:
    iAmVortix = true
    iAmVortix = nil
    
    Operations on data

    Data isn't much good to us if we can't do anything to it. If all programs just stored information we gave it and printed it back to us, it we wouldn't need computers to do it. The point of using a computer is to process the data. All operations in Lua require either one or two pieces of data. There are many operations which work on only one type of data - requiring two numbers, or two strings, but not one of each, for example. There are also those that work on all types of data. Let's start with those.

    Operations require an operator: some symbol, or group of characters, which indicate to Lua that you're trying to perform that operation. So, as I introduce you to the range of operations available in Lua, you'll also have to remember the operators that enact them. The first case to look at is the equality operator, signified by two equals signs (==). This is not the same as earlier, where we used a single equals sign in assigning a value to a variable. In this case, we're asking a question: the question of whether the two items on either end of the operator are the same. The program answers this question by the way of a boolean, with true representing "yes", and false representing "no". Have a look below:
    Code:
    print(2019 == 2018)
    That will print false, because 2019 is not the same as 2018. The following will print true, however:
    Code:
    print(2019 == 2019)
    We can also ask if two items are not equal to each other using the not equal to (~=) operator. If you replace the == with ~= in the examples above, you will get the opposite boolean value printed out to what you got before. These work on all types of data, and so we can use them to compare a number with a string. It's important to note that items with different types are always regarded as different when you check equality, so the following will print false:
    Code:
    print(5 == "5")
    Next to the equality operators, the only other type of operator which supports all types of data is the logical operator. A logical operator is one driven by boolean values. In Lua, every value - string, number, or otherwise - has an implicit boolean value. This works through a process known as type coercion, in which Lua will convert a value of one type to another in order to allow it to fit in the given context. This is used by logical operators, because they are designed to take booleans. Values which are convert to the boolean true are called truthy. Similarly, values which convert to the boolean false are known as falsey (or falsy, if you prefer). There are three logical operators:


    • and: This operator takes two values. If the first value is truthy, then the second value is the result. If it is falsey, then it is the result.
    • or: This operator takes two values. If the first value is truthy, then it is the result. If it is falsey, then the second value is the result.
    • not: This operator takes one value. If the value is truthy, then boolean false is the result. If it is falsey, then boolean true is the result.
    How do we convert a piece of data to a boolean, then? Well, if it's not false, and it's not nil, it converts to true. Otherwise, it converts to false. Let's see some examples of the logical operators:
    Code:
    print(false and false) -- Prints false. The first value is falsey, so it is the result.
    print(false and true) -- Prints false. The first value is falsey, so it is the result.
    print(true and false) -- Prints false. The first value is truthy, so the second is the result.
    print(true and true) -- Prints true. The first value is truthy, so the second is the result.
    print("hello" and "goodbye") -- Prints "goodbye". The first value is truthy, so the second is the result.
    print(nil and "goodbye") -- Prints nil. The first value is falsey, so it is the result.
    
    print(false or false) -- Prints false. The first value is falsey, so the second is the result.
    print(false or true) -- Prints true. The first value is falsey, so the second is the result.
    print(true or false) -- Prints true. The first value is truthy, so it is the result.
    print(true or true) -- Prints true. The first value is truthy, so it is the result.
    print("hello" or "goodbye") -- Prints "hello". The first value is truthy, so the second is the result.
    print(nil or "goodbye") -- Prints "goodbye". The first value is falsey, so the second is the result.
    
    print(not true) -- Prints false. The value is truthy, so false is the result.
    print(not false) -- Prints true. The value is falsey, so true is the result.
    print(not "hello") -- Prints false. The value is truthy, so false is the result.
    print(not nil) -- Prints true. The value is falsey, so true is the result.
    
    I think that's it for the operators which work on all types. Let's have a look at numbers, now. The most obvious thing we can do with them is calculations: addition, subtraction, negation, multiplication, division, and exponentiation. Examples are shown below, based on our earlier currentYear definition:
    Code:
    print(currentYear + 1)  -- Addition: the value of currentYear plus 1
    print(currentYear - 1) -- Subtraction: the value of currentYear minus 1
    print(-currentYear) -- Negation: the value of currentYear with the opposite sign
    print(currentYear * 2) -- Multiplication: the value of currentYear times 2
    print(currentYear / 2) -- Division: the value of currentYear divided by 2
    print(currentYear ^ 2) -- Exponentiation: the value of currentYear to the power of 2
    
    Lua 5.2 and above implements another form of division operator, called the integer division operator. This will give you the rounded down form of regular division:
    Code:
    print(currentYear // 4) -- Integer division: the value of currentYear divided by 2, rounded down
    GMod's Lua is based off of Lua 5.1, though, so you won't see this in that context.

    We can also get the modulo of a number (the remainder after you divide one number with another):
    Code:
    print(currentYear % 4) -- Modulo: the remainder when we divide currentYear by 4
    Each of these arithmetic operators not only works on numbers, but can sometimes work on strings too! If the string represents a number, type coercion occurs - converting the strings to numbers (unlike earlier, where we saw coercion into booleans for evaluation by the logical operators). An example:
    Code:
    print("502" + 7) -- Prints 509.
    Next, we have the relational operators. With numbers, it is meaningful to ask where one number is relative to another - specifically, which one is greater or lesser. Just like with equality, these operators result in a boolean value based on whether the question we are asking and the numbers we're asking them about. Let's look at some examples using our previous definition of currentYear:
    Code:
    print(currentYear < 2020) -- Here we're asking "is currentYear less than 2020?". This prints false.
    print(currentYear > 2020) -- Here we're asking "is currentYear greater than 2020?". This prints false.
    print(currentYear <= 2019) -- Here we're asking "is currentYear less than or equal to 2019?". This prints true.
    print(currentYear >= 2018) -- Here we're asking "is currentYear greater than or equal to 2018?" .This prints true.
    
    Now, to strings. There are two key operators for strings: the concatenation operator, and the length operator. Concatenate is a word meaning "join together". The string concatenation operator takes two strings, and joins them. Have a look:
    Code:
    introduction = "Hello"
    name = "Vortix"
    
    print(introduction .. name) -- The double dots enact concatenation.
    
    That's great: it prints "HelloVortix". We still want a space in, though. Fortunately, we can chain the results together. Since concatenation produces a string, we can use the operator twice in order to join a string to the result of a concatenation, like so:
    Code:
    introduction = "Hello"
    name = "Vortix"
    
    print(introduction .. " " .. name)
    
    This prints "Hello Vortix". Much better! Let's move on to string length.

    The string length operator takes a string, and tells us how many characters it has, as follows:
    Code:
    name = "Vortix"
    
    print(#name) -- The hash sign before the variable name gets its length.
    
    There is a specific order in the way that Lua handles these operators: some operators have more precedence than others. Some operators do have the same priority, though: Lua handles them from left to right in these cases. The order of operations can be found here. If you are writing an expression and the order of operations is not clear, it can be useful to clarify. You can use brackets to group things together and show your intention. For example:
    Code:
    first and (second or third)


    Comments
    In programming, clarity is essential. We should write our programs so that they can be easily understood by other people - even if other people will never see them. You might find yourself re-visiting past work at a later date, and if you don't make everything clear you may not understand your own code. If you feel that there is information that would be useful for a reader to know that is not clear from your code, you can add a comment. Comments are regions of text in a program which will not be interpreted as code. We mark a comment by use of two dashes, --. For example:
    Code:
    iAmVortix = true -- this is my comment
    You can also define a comment on multiple lines by using the double dash followed by the notation for a multi-line string. For example:
    Code:
    iAmVortix = true --[[
        This is my multi-line comment.
    ]]
    

    Conditionals
    Conditionals allow us to execute different code, based on what value it is given. Here's an example:
    Code:
    if currentYear == 2019 then
        print("First")
    elseif currentYear >= 2020 then
        print("Second")
    else
        print("Third")
    end
    
    We call the structure seen above an if statement. It contains one or more condition values, as well as an associated code block for each condition. Each code block is easily visible, as it is conventionally indented through the use of either spaces, or one tab (I prefer tabs, personally). You separate code blocks by the presence of the if, elseif, else, and end keywords. A code block in an if statement can be started through the use of an if, elseif, or else. The if keyword is used for the initial condition. Any subsequent conditions are indicated by the use of an elseif. Finally, the else is used to catch all cases where none of the previous conditions were met. A code block is ended by either the start of a new code block, or the end keyword. So, the if statement shown above can be translated into "if currentYear is equal to 2019, then print 'First'. Otherwise, if it's greater than or equal to 2020, print 'Second'. If it is neither, print 'Third'". Also note that where I say a code block is ended by the start of a new code block, I mean of the same if statement. If an if statement were to contain another if statement (as shown below), the inner if statement would not affect the outer one.
    Code:
    if condition then
        if condition2 then
            print("hi")
        end
    end
    
    I've left it a bit vague so far, though: what do I mean by "condition value"? I mean any value between the (else)if and the then. A code block is run if its associated condition value is truthy. Otherwise, it isn't.

    What if we want to set some variable's value to one thing or another based on some condition? Based on the current topic, one might think of the following:
    Code:
    if condition then
        myVariable = "first"
    else
        myVariable = "second"
    end
    
    That is a perfectly reasonable thought, and a perfectly reasonable thing to do - in fact, it will work. Sometimes, if the variable name and condition are concise enough, it might be better to use logical operators, though. Remember that the and operation will result in the second value if the first is truthy, and the second if it isn't? And the or operation will result in the first value if it is truthy, and the second if it isn't? Well, we can combine the functionality of these two to form a one-line if statement for cases where we are using a condition to set a variable! See below:
    Code:
    myVariable = condition and "first" or "second"
    
    So, what's happening here? Well, if condition is truthy, the second value of the and is the result. This is "first". If the condition is falsey, the first value is the result, which is condition. Either way, it is then used as part of the or operation. In the case where condition was the result of the and operation, it will be falsey and so the second value of the or will the result: "second". Careful, though - if using this trick with values other than "first" and "second", make sure that the replacement value for "first" is always truthy. If it isn't, and it is the result of the and operation, it will lose in the or operation and so the second value will be the overall result.

    Scope
    With a greater understanding of code blocks comes the discussion of scope. It is easiest to grasp the idea by understanding that it has two kinds: local, and global. To ease the learning process up to this point, I've told you that all you have to do to make a variable is the following:
    Code:
    myVariable = value
    And it's true, but that's not the only way. What we did initially is create a variable globally. That means that the variable is available in every part of every file that we run, once we create it. If we were to create a variable locally, it would only exist within the code block it is created in, as well as any sub code blocks. We define such a variable like so:
    Code:
    local myVariable = value
    This is, in fact, the way that you should be defining them, unless you specifically need the variable to be available elsewhere. This is partly because by reducing the scope of the variable, you also reduce the code which can mess with it. So, if a problem arises, you already know where the problem isn't. Here's an example of scope:
    Code:
    if true then
        local localVariable = "Hello"
        globalVariable = "Hi"
    end
    
    print("Global variable: " .. globalVariable)
    print("Local variable: " .. localVariable)
    
    What do you expect will print out here? The if statement will run for sure, since I have set its condition to true. Then, within the code block of the if statement, a local variable is created, as well as a global one. Afterwards, "Global variable: Hi" will print out, followed by an error. This is because the global variable can exist outside of the code block it was created in, but the local variable can't - so it has a value of nil. Of course, Lua doesn't know how to concatenate nil with a string, so it spits out an error.

    Repetition

    We can also create blocks of code which repeat themselves. Let's say we want to repeat "Hello Vortix" five times. Other than by writing out a print five times, there are three primary ways of achieving this. The following is called a for loop:
    Code:
    for i = 1, 5 do
        print("Hello Vortix")
    end
    
    That code does the following:


    1. Creates a variable i, which will only be available in the scope of the loop, and sets its starting value to 1.
    2. Runs the code block.
    3. Checks if the value of i has surpassed the limit specified (5 in this example).
    4. If it has, execution is done. Otherwise, it increments the value of i by some step value, which is 1 by default.

    The step value is just a value which can be optionally specified in the for loop that says how much should be added to i after each iteration. It can be specified as follows:
    Code:
    for variable = startingValue, limit, step do
        code block
    end
    
    Here's the second way we could approach this problem:
    Code:
    local counter = 1
    
    while counter <= 5 do
        print("Hello Vortix")
    
        counter = counter + 1
    end
    
    Shown above is a while loop: it repeats while the condition specified is met. As long as it is given a truthy condition, it will keep repeating.

    Next is the repeat until loop:
    Code:
    local counter = 1
    
    repeat
        print("Hello Vortix")
    
        counter = counter + 1
    until counter > 5
    
    This repeats until a specified condition is met. As long as the condition is falsey, it will keep repeating.

    We can also force-escape a loop using the break keyword. Take the following example:
    Code:
    for i = 1, #myTable do
        -- code
    
        if condition then
            break
        end
    end
    
    In this case, whenever condition is truthy, the break will run and so we will force-exit the loop.

    Tables
    Now for a new type of data: tables. A table is a collection of values, we can create a table through the use of curly brackets:
    Code:
    myTable = {}
    Shown above is the creation of an empty table. If we want to store values in this table, we can put them within the braces, comma separated, in the order we want to store them:
    Code:
    myTable = {"hello", "hi", "goodbye"}
    Each value in this table is assigned an index: some value we know which we can use to look up our chosen value in the table. When tables are created like this, Lua automatically assigns each of the values a number, starting from 1, and incrementing by 1 each time. So, "hello" has an index of 1, "hi" has an index of 2, and "goodbye" has an index of 3. To use an index to get a value from a table, we simply surround that index in square brackets after referencing the table. Below is an example of printing the first item in the table:
    Code:
    myTable = {"hello", "hi", "goodbye"}
    
    print(myTable[1])
    
    We can also set the value at a given index outside of the table's creation, like follows:
    Code:
    myTable = {"hello", "hi", "goodbye"}
    
    myTable[2] = "bye"
    
    This would replace "hi" with "bye" in the table.

    Although Lua automatically assigns items in the table numbers when we create them the way we did, that's not the only way of doing things. The index doesn't have to be a number: in fact, it can be any non-nil value. In order to tell Lua what index we want to use for a given value, I- well, I'll just show you:
    Code:
    myTable = {
        ["hello"] = "hi",
        "goodbye"
    }
    
    In this example, "goodbye" has an index of 1, and "hi" has an index of "hello". Note that the new lines I added are just for display purposes: they don't affect the outcome of the script.


    To access a key with a specifically chosen index, we do the same as we did before:
    Code:
    print(myTable["hello"])
    If that index happens to be a string which conforms with the variable naming rules, we can actually just put a dot after our table followed by the index:
    Code:
    print(myTable.hello)
    Just like strings, tables can use the length operator. The length operator doesn't get the number of items in the table, but rather the number of sequential indexes starting at 1 which have values associated with them, until we hit nil*. For example, see below:
    Code:
    myTable = {"hello", "hi", "goodbye"}
    
    print(#myTable)
    
    The code would print 3. Starting with 1, we can see index 1 has a non-nil value ("hello"). Then, we can see that index 2 does as well ("hi"), and we can see that index 3 does too ("goodbye"). Index 4 refers to a nil value, so we stop there: there are three values at consecutive indexes, starting at 1.

    Lua defines a global table, _G, which contains all global variables (including itself!). We can even use this to access global variables:
    Code:
    _G["myVar"] = value -- equivalent to myVar = value
    Don't do this unless you need to, though: it clouds your intentions if used unnecessarily, and reduces readability (and increases execution time, in some cases).

    * This isn't entirely accurate, but will be for most use cases. I may provide an explanation as to the true behaviour in a future section, but I will leave it for the moment as it requires a deeper understanding of Lua.

    Functions

    Another new type of data: functions. A function is essentially a code block which can be stored and used over and over again. In fact, that's exactly what print is. Lua defines print to be able to take input and send it back as output, so whenever you use print you are causing some pre-defined code to run. We can define a function as follows:
    Code:
    local myFunction = function()
        -- code block
    end
    
    Here's an example function:
    Code:
    local printIntroduction = function()
        print("Hello, I'm Vortix")
    end
    
    When writing code, it's good to design it to be as flexible and re-usable as possible. For example, what if we want an introduction for someone of a different name? Fortunately, functions can take data as inputs as well:
    Code:
    local printIntroduction = function(name)
        print("Hello, I'm " .. name)
    end
    
    You can then call this function (make it execute) by putting brackets after the name of the variable holding it. The brackets should contain any information to be given to the function, like so:
    Code:
    printIntroduction("Vortix")
    Each piece of data being passed into a function is known as an argument or parameter. Functions can take any number of arguments, like so:
    Code:
    local myFunction = function(argument1, argument2, ...)
        print("Hello, I'm Vortix")
    end
    
    I used the ellipsis in the above example to indicate that you can have as many arguments as you want, and Lua actually interprets it to mean that. This is called variable arguments (varargs), and an example is shown below:
    Code:
    local printItems = function(...)
        local items = { ... };
    
        for i = 1, #items do
            print(items[i])
        end
    end
    
    As can be seen above, I've defined a function named printItems, which takes any number of arguments and prints them each out individually. It's important to note that the ellipsis doesn't represent an item of any data type: it literally represents multiple independent items, and anywhere you use the ellipsis, it will act as if you had put multiple values in its place. That's why I take the ellipsis and surround it with curly braces: this causes a table to be created with the values the ellipsis represents, each argument being assigned an integer index. I then create a for loop to iterate over each argument and print it.

    A function can also send values back to the place where it was called. An example is shown below:
    Code:
    local multiply = function(firstNumber, secondNumber)
        return firstNumber * secondNumber
    end
    
    If you were to call multiply with two numerical arguments, you would be given back the result:
    Code:
    local product = multiply(5, 3)
    
    print(product) -- Prints 15
    
    You can also return as many values as you want. Just like arguments, these value will be treated as independent. How does one deal with a function that returns multiple values, though? We've got two options: we could do what I did in the varargs example, and store it in a table, or we define multiple variables on the same line, to match the multiple returns. An example:
    Code:
    local flip = function(firstNumber, secondNumber)
        return secondNumber, firstNumber
    end
    
    local first, second = flip(5, 3)
    
    print(first)
    print(second)
    
    The above code will print 3 and 5.

    Now I will introduce a new (preferred) way of making functions, shown below:
    Code:
    function myFunction(arguments)
        -- code
    end
    
    That is equivalent to the below code:
    Code:
    myFunction = function(arguments)
        -- code
    end
    
    We can even specify scope. If you want a local function using the new syntax, you just prepend local:
    Code:
    local function myFunction(arguments)
        -- code
    end
    


    Now imagine you have a table, myTable, and you want to define a function at the myFunction index of that table. The standard definition would look like follows:
    Code:
    function myTable.myFunction(arguments)
        -- code
    end
    

    (Note: you cannot use the local scope modifier if you're creating a function at a table index, because its scope is determined by the scope of the table)
    Lua introduces some shorthand for those who want the function to take the table which it belongs to as a parameter. If you use a colon instead of a dot before the index which the function will preside, the keyword self will refer to the table. That may sound complicated, but hopefully an example will clear things up:
    Code:
    function myTable:myFunction(arguments) -- equivalent to: function myTable.myFunction(self, arguments)
        -- code. Within here, self == myTable
    end
    
    In reality, what's happening here is that the table is being passed in as the first argument. Due to the fact that the colon notation is used, Lua hides that argument and allows you to refer to it as self instead. This is mainly to help users used to Object Oriented Programming.
    So, what does a call to this function look like? Well, it looks like the following:
    Code:
    myTable.myFunction(myTable, arguments)
    
    Like with defining the function, though, we can also use colon notation to call the function and save ourselves from having to pass in myTable manually:
    Code:
    myTable:myFunction(arguments) -- equivalent to: myTable.myFunction(myTable, arguments)
    
    One more syntactical note about function calls. If we have one argument to pass in, which is either a string or a table, and we're passing in the item as its created (rather than through a variable), then we don't need the parentheses to call the function. For example:
    Code:
    print "hi" -- versus print("hi")
    print[[hi]] -- versus print([[hi]])
    print{ 1 } -- versus print({ 1 })
    
    A couple of afternotes about varargs:

    • If you encase them in brackets, only the first value counts. So, for example, someFunction(...) will pass an argument into someFunction for each value represented by the ellipsis. However, someFunction((...)) will only pass the first value represented by the ellipsis, and all secondary values will be dropped: the grouping brackets only return a single value (distinct from the outer brackets, which are simply used to mark the function call).
    • If there is some item after them in an expression, all but the first value of the varargs will be dropped. For example, if you do "return ..., someValue" within a function, only the first value of the vararg will be returned, and the second value returned will be someValue. You can try it out:
    Code:
    function myFunction(...)
    
        return ..., 7
    end
    
    print(myFunction(1,2,3))
    Also note: print prints the returns, spaced out, on the same line, because it is a varargs function. Vararg functions do have a specific label: variadic. It comes from the word adicity (for which arity is a common synonym), which means the number of arguments a function has. As such, you may see words such as dyadic (two arguments), monadic (one argument), and niladic (no arguments).


    Repetition, revisited
    With the knowledge of tables and functions behind us, we can take another look at repetition. Specifically, the for loop. I earlier showed you the following form of for loop:
    Code:
    for variable = startingValue, limit, step do
    But, there is another form:
    Code:
    for index in iterator, generator, control do
    In this form, the iterator is a function which should take take two parameters: an input table, and an optional index. For a given input table, the iterator will generate an internal ordering of its indexes, and it will return the index in its order that comes directly after the index specified as the second parameter. If no index is specified, it returns the first index in its ordering. The generator and control are the starting parameters for this iterator function: the table and index, respectively. The value returned by the iterator is used as the index seen in the loop structure. After each run of the loop, Lua takes the index value that was used in the current loop, and passes it in as the second argument to iterator, whilst still passing the original generator in as the first argument. This, in effect, means that the loop will cycle through all possible indexes of the table, opposed to the previously known form of for loop which will only cycle through integer indexes. If all of this is confusing, don't worry, we'll do a run-through of an example shortly.

    Lua has a built-in iterator function called next. As required, it takes an input table and a "skip past" index. Let's experiment with this:
    Code:
    local myTable = { "a", "b", "c", "d", "e" }
    
    print(next(myTable))
    
    The next iterator will first go through all integer indexes in-order before it starts returning non-integer ones. So, it will go through the characters in myTable in the order we specified them. If you run the code above, you may find that it prints out two values together: 1 and "a". This is because print is a vararg function, and next returns the value at the index it is returning as well as the index itself. Now, if we take the index we've been provided (1) and pass it in as the second parameter to next, it should give is the next index,value pair:
    Code:
    local myTable = { "a", "b", "c", "d", "e" }
    
    print(next(myTable, 1))
    
    So, the above should print 2 and "b". Let's look at an example of how we'd use this in a for loop:
    Code:
    local myTable = { "a", "b", "c", "d", "e" }
    
    for index in next, myTable do
        print(index)
    end
    
    So, we're giving the loop the next iterator, and telling it to start with a generator (first parameter to the iterator) of myTable. We haven't a control (index to skip past), so we're starting from the beginning with this table. We effectively get the following executing on the first run-through of the loop:
    Code:
    index = next(myTable)
    This will give us the index 1 in the case of our example table. Our print will run, and print out that index for us. Next, we will use the current value of index as the second parameter to call next again:
    Code:
    index = next(myTable, index)
    This means we get index 2 on our second run-through. The cycle continues.

    Now, you may remember that I said that next also returns the value of the item at the given index. We can make use of this. At a minimum, any iterator is required to return the next index in its ordering, but we can support more than just that return value. Any values returned by the iterator will be set to the variables specified before the in keyword. Given that next returns both an index and value, we can take both of these in our for loop:
    Code:
    local myTable = { "a", "b", "c", "d", "e" }
    
    for index, value in next, myTable do
        print(index, value)
    end
    
    Whilst the value variable doesn't serve any purpose in the actual structure of the loop, it does provide a convenient way to loop through each value in the table whether it has an integer index or not.

    Just for clarity, I'll show you what happens if we specify a control value off the bat:
    Code:
    local myTable = { "a", "b", "c", "d", "e" }
    
    for index, value in next, myTable, 2 do
        print(index, value)
    end
    
    In this case, the first run-through will trigger this:
    Code:
    index = next(myTable, 2)
    This means that the next iterator will skip past 2, and so we will start at the third index, which has a value of "c".

    There are two functions which Lua implements to make this looping process easier for us: pairs and ipairs. pairs returns the next iterator, and the table it is given, so it is the equivalent to setting up the for loop without the control value. It would look like so:
    Code:
    for index, value in pairs(myTable) do
    In the case of ipairs, the next iterator is not used. The ipairs iterator will loop through only integer indexes, and will stop when it finds a nil value. So, this function won't loop through string indexes, like pairs will:
    Code:
    for index, value in ipairs(myTable) do

    Standard libraries
    Lua provides many useful functions out of the box. We call collections of these functions libraries. The following standard (built-in) libraries exist:
    • The math library. There is a pre-defined table in Lua named math which contains several useful functions and values relating to mathematics. An example of this would be the math.sin function, which takes a value in radians and returns the sine of it. An example of a constant value (as opposed to a function) stored by Lua is math.pi, which is a number representing pi.
    • The string library. This is implemented as a pre-defined table named string, which contains string utility functions. An example of this is string.upper, which takes a string and converts it to uppercase.
    • The table library. This is implemented as a pre-defined table named table, which contains table manipulation functions. An example of this is table.insert, which will add the value it is given to the next integer in the index sequence. This means that you don't have to perform myTable[#myTable + 1] = value to add a value to the end of the table.
    • The io (standing for input-output) library. This is a library used for file manipulation in Lua.
    • The os library. This is implemented as a pre-defined table named os, which contains functions responsible for interfacing with the operating system. An example of this is os.time, which returns the Epoch time of the machine it runs on.
    • The debug library. This is implemented as a pre-defined table named debug. It is a very powerful library for getting information about execution. You can, for example, use debug.getinfo to get a table of general information about a function, including the file and line number it is defined in.
    Continuation
    This post has reached the size limit, and so I can't fit everything into one post. Click here to see part two.
     
    • Like Like x 2
    • Good Coder Good Coder x 2
    • Winner Winner x 1
    Last edited: Feb 17, 2019
  2. Vortix

    Vortix Legend Clockwork Customer Active Member

    This is a Clockwork development primer, so I hope to gradually work on this and produce explanations regarding GMod and Clockwork soon, but as of right now this only has Lua content. If you are unfamiliar with Lua I hope this can be of some help, and if you are familiar this may shed light on some behaviours you were unaware of!

    Please let me know if any sections in particular seem confusing, and I will do my best to make them clearer.
     
    • Winner Winner x 2
  3. Aspect

    Aspect =) Veteran Active Member

    How do I like a post twice

    Also, I thought the contents of 'if' need to be inside parentheses. So,
    Code:
    if (blah blah) then
        Bluh
    end;
    Also I thought end needs a ; at the end, as shown above.

    Or are those just Clockwork-Only?
     
    • Friendly Friendly x 1
  4. Vortix

    Vortix Legend Clockwork Customer Active Member

    At the end of "Operations on data", I mention that brackets can be used to group things together: that is all you're seeing in the case of the if statement. The example I used was the following case:
    Code:
    a and (b or c)
    In this statement, brackets are used to force a specific order of execution of the operations. The and operator sees the two values it needs to operate on: a, and whatever is produced within the brackets. That isn't the only time parentheses may be used for grouping, though: there is also a purely visual application. Sometimes, an example may behave as you want just by following Lua's order of operations, but you use parentheses regardless to increase readability. The parentheses you see in the if statement are simply a visual grouping, to that end. Parenthesised if statements are a coding standard employed by Clockwork, and I tend to include them in my work out of personal preference, but they aren't a necessity.

    Regarding the use of the semi-colon, it is an optional character. Some languages (often C-based ones) require the aid of a semi-colon to indicate the end of the line. Lua does not require such assistance, but does optionally accept the semi-colon for clarity purposes. Again, it is a standard used by Clockwork, but it is not required.
     
    • Informative Informative x 1
    Last edited: Feb 3, 2019
  5. Aberidius

    Aberidius Chief Technology Officer Staff Member Administrator Store Support Active Member

    Stickied for good effort.
     
    • Agree Agree x 1
    • Friendly Friendly x 1
  6. Sixx

    Sixx presidential #1 Legend

    i was gonna give you legendary even before you posted this :rolleyes: great work man, I might do something similar on C# soon if I stop being retard level lazy
     
    • Friendly Friendly x 1
  7. Vortix

    Vortix Legend Clockwork Customer Active Member

    Some updates:
    • I've updated the Repetition section to describe the break keyword.
    • I've added a bit to the end of the Tables section, describing the globals table, _G.
    • I've added some information to the Functions section, describing some particularities of using the varargs ellipses, as well as mentioning some terminology.
    • I've added the Standard libraries section.
     
  8. Aspect

    Aspect =) Veteran Active Member

    You should underline or change the color of the additons so we can tell what's new easier!
     
  9. Vortix

    Vortix Legend Clockwork Customer Active Member

    I expect I may make more revisions in the future, as I recall more information that may be relevant to include. However, each of the additions is located at the bottom of its respective section, so you'll be able to find them fairly easily by just scrolling to the end of whatever section you're looking for updates in.
     
    • Informative Informative x 1
  10. EliTheGamerFTW

    EliTheGamerFTW Clockwork Customer Active Member

  11. Vortix

    Vortix Legend Clockwork Customer Active Member

    Clockwork Development Primer (Part 2)
    Lua (continued)
    Userdata
    So far, we've discussed structures which are pre-designed with specific layouts: strings, numbers, tables, functions. Lua also provides its host language with the ability to define its own structures, and have those structures accessible in Lua, through the userdata type. For example, a game developer using Lua with C++ may create a new userdata item to represent a player. Then, from Lua, a scripter can take advantage of this custom structure. Defining the structure in C++ rather than Lua directly (through the use of a table) allows increased memory efficiency, higher access speeds, and easy interaction with the structure from within the host language (C++ in our example).

    Metatables

    Part of the wonder of Lua is that it is extremely flexible - it tries to impose a minimal amount of restrictions on the developer, and even gives developers the means to change its own behaviour through Lua itself! We can do this through the use of a specific case of table known as a metatable. When performing certain operations, Lua checks to see if the piece(s) of data being operated on have a metatable. If they do, it performs the behaviour described by that metatable instead of its regular behaviour. For example, take addition. We know that addition is an operation defined on two numbers (or coerced strings). We can, in fact, take control of this operation, and make it take the data we want. Let's see this in action:
    Code:
    local myTable = {}
    local myMetaTable = {
        __add = function(self, addingValue)
            return #self + addingValue
        end
    }
    
    setmetatable(myTable, myMetaTable)
    
    print(myTable + 0)
    I start by defining myTable: the piece of data whose behaviour I want to change. I then define myMetaTable, which describes how I want to change the behaviour. I use the setmetatable function to set myTable's metatable to myMetaTable. Finally, I perform the addition. Instead of getting an error, as one might expect, it works: we get 0. This is because __add is a special index of the metatable which is checked by Lua in cases of addition. We call the function stored at such an index a metamethod. The first parameter of the metamethod is always the object which the metamethod describes: I've named it self here. The second parameter to the metamethod is the other parameter of the (addition, in this case) operation. I have defined in this example that if my table is used in addition, the addition will be completed using its size. So, if I add an item to myTable, the result will change.

    The obvious next question is "what metamethods are available?". So, here they are:

    • As we've just seen, the __add metamethod can be used to override addition (+). Lua checks to see if either item being added has a metamethod, and, if it does, it runs that to get the result of the addition.
    • The __sub metamethod can be used to control subtraction (-).
    • The __mul metamethod can be used to control multiplication (*).
    • The __div metamethod can be used to control division (/).
    • The __mod metamethod can be used to control modular arithmetic (%).
    • The __pow metamethod can be used to control exponentiation (^).
    • The __unm metamethod can be used to control unary minus (-, negation).
    • The __concat metamethod can be used to control concatenation (..).
    • The __len metamethod can be used to control length getting (#).
    • The __eq metamethod can be used to control equality checking (==).
    • The __lt metamethod can be used to control less than checking (<).
    • The __le metamethod can be used to control less than or equal checking (<=).
    • The __index metamethod can be used to intercept index lookups on a table if the table doesn't have a value at the given index. For example, in the case of myTable[5], if index 5 is not defined for myTable this will be forwarded to the __index metamethod, and you will be able to return a substitute value.
    • The __newindex metamethod can be used to intercept index setting on a table if the table doesn't have a value at the given index. For example, in the case of myTable[6] = true, if index 6 is not defined for myTable this will be forwarded to the __newindex metamethod and you will be able to set your own value.
    • The __call metamethod can be used to introduce function calls to a table. If one is defined, then myTable(args) can be used.
    These metamethods exist in Lua 5.1, but this list expands when looking at Lua 5.3 - I won't be covering those metamethods in this primer.

    Note: in the case of "greater than" or "greater than or equal" comparison, we use the "less than" or "less than or equal" metamethods but provide different arguments.

    You may have noticed a theme with these metamethods: they are prefixed by a double underscore (__). This allows them to be suitably distinguished from regular, non-metamethods: to allow them to exist without being likely to impact any non-metamethods that the user may choose to define with the same name on the metatable.

    There are three metamethods I want to go into more depth on: __index, __newindex, and __call. Let's start with the latter. The __call metamethod has the following usage:
    Code:
    local myTable = {}
    local myMetaTable = {
        __call = function(self, ...)
            print(...)
        
            return true
        end
    }
    
    setmetatable(myTable, myMetaTable)
    
    local result = myTable(1, 2)
    
    print(result)
    The above code defines the call behaviour of myTable to print out any arguments it is given, and return true. As such, 1 and 2, followed by true on a new line, will be printed here. As stated before, the metamethod takes the data whose behaviour is being changed as the first parameter. For any subsequent parameters in this case, they are defined as the arguments passed in the call. Without this metamethod present, an attempt to call myTable would fail, as it is a table rather than a function.

    Now let's look at __index:
    Code:
    local myTable = {}
    local myMetaTable = {
        __index = function(self, index)
            return index
        end
    }
    
    setmetatable(myTable, myMetaTable)
    
    local result = myTable[5]
    
    print(result)
    I've defined this metamethod to ensure any index lookup results in the index itself, so in this case 5 would be printed. It is key to remember, though, that this method is only called when there is no value at the given index: if we had defined a value at index 5 prior to setting the metatable, that value would be returned instead.

    What, then, if we need to bypass __index, and obtain the value actually stored at the given index? For that, we use rawget. The use of rawget won't result in a call to __index. It is used as follows: rawget(table, index).


    For __newindex, I want to make a similar point. If you want to set a given index, without risking __newindex being called, you can use rawset to bypass it. Call it like so: rawset(table, index, value).

    GLua

    What is GLua?
    GLua is a modification of Lua 5.1, and is developed for and used by GMod (ergo the name).

    What changes between Lua 5.1 and GLua?

    C stylings:
    GLua introduces some stylings from C. This includes the following:

    • Single line comments can use // instead of --
    • Multi-line comments can use /* and */ instead of --[[ and ]]
    • Inequality can be checked using != instead of by ~=
    • Logical inversion can be performed using the ! symbol in place of the not operator (and no trailing space necessary)
    • Logical and can be performed using &&
    • Logical or can be performed using ||

    Iteration skipping:
    GLua introduces the continue keyword, which is designed for use in loops. If continue is used within a loop, the current iteration will skip. For example:
    Code:
    for i = 1, 6 do
        if (i == 3) then
            continue
        end
    
        print(i)
    end
    The code above will print all numbers inclusively from 1 to 6, except 3: this is because that iteration is skipped, as per the continue statement in the condition.

    Library function removals:
    Many functions in Lua 5.1 aren't present in GLua. This is for security purposes: scripts in GMod don't need system-wide access, and should stick solely to affecting GMod. A large portion of the os library and some functions of the debug are unavailable for this reason, and the io library was removed completely.

    Function overrides:
    GLua overrides the type function, in order to define custom types for their userdata objects. For example, running the type function on a player will return "Player" in GLua, however the standard type function would return "userdata". To check if it should return a custom type, the GLua type function checks if the item is userdata, and if it is, it checks its metatable for the MetaName property. If the MetaName property is a string, it returns that. If the property is a number, it converts it to a string and returns that. Otherwise, if the property is neither, it returns "UserData". For non-userdata types, the type function functions the same as Lua 5.1's.

    GLua also overrides the require* and module* functions.

    * These concepts are not yet covered in this primer, but I hope to revise it to include these topics in the future. For now, this section serves as an informational for those who know of the topics already, or for those who are interested in learning them independently.

    Code inclusion:
    The dofile*, loadfile*, and loadstring* functions for loading and executing code chunks are not present in GLua. Instead, GMod implements an include function as a substitute for dofile, and a CompileFile and CompileString function as a substitute for loadfile and loadstring respectively.

    * These concepts are not yet covered in this primer, but I hope to revise it to include these topics in the future. For now, this section serves as an informational for those who know of the topics already, or for those who are interested in learning them independently.
     
    • Good Coder Good Coder x 1
    Last edited: Feb 19, 2019
  12. Rhenz

    Rhenz Who needs a map? Staff Member Moderator Crusader Veteran Active Member

    What blows my mind is that I could have never understood any of this three years ago and yet here I am -- able to read everything here.

    Welcome back, Vortix. And well done with this. Solid work.
     
    • Like Like x 1
    • Friendly Friendly x 1

Previous Readers (Total: 0)