• RE: Basic Admin Commands

    First off, don't post screenshots of the code. This makes it impossible to copy and paste it and make necessary changes. Your string colour is so dark I can't even read it.

    Second off, your CheckAdmin function is going to immediately return false if a player joins but their UserId is not first in the list. Even if it's an admin.

    Also, the IDs should already be a number, calling tonumber is just redundant.

    function module.isAdmin(client) --// 'is' is better than 'check'
        for _, id in ipairs(Admins) do
            if (rawequal(client.UserId, id)) then
                return true;
            end
        end
        return false;
    end
    

    If the return false; statement is reached that is because the above was not fulfilled; the player is not an admin.

    Also, you should not be handling admin commands client sided. An exploiter can manipulate the requirements this way.

    Finally, you are not using len correctly. It is used string.len(s) or s:len(). Again, this is a number, you don't need to call tonumber. Either way, don't use it: we have the unary length operator #. Use #s.

    So what did we learn today?
    Events (Player.Chatted)
    Module Scripts(require, etc.)
    Tables (Admins,Indexing,etc.)

    Your thread does not revolve around events, module scripts or tables. You listened for events, yes, but your thread isn't about events! Same for everything else.

    posted in Tutorials
  • RE: Libraries+, an extension to the libraries

    17/04/19:

    getReciprocal has been simplified. I did some research, the multiplicative inverse = reciprocal, and the reciprocal of n is 1/n (assuming n != 0).

    Here is what it looks like now:

    function LibrariesPLUS.maths.getReciprocal(num)
        if (rawequal(num, 0)) then
            return nil; --/* for nerds who try division by 0 */
        end
        return 1/num;
    end
    
    posted in Cool Creations
  • Functions: what they are, and how to use them
    i'm going to copy phlegethon's idea shoutout to @Phlegethon5778

    Disclaimer

    I will not be using ROBLOX. Most if not all of the stuff I will teach should be applicable to ROBLOX, but this is not meant for ROBLOX.

    The beginning: what are functions?

    Functions are just containers of reusable code. They are also first class values, meaning variables (local and global) can be assigned to them, tables can contain functions, they can be passed around as arguments to functions, and returned by functions.

    Say you have a loop that prints the factors of a certain number. You might write it like so:

    (in this case I want to get the factors of 10)

    for i = 1, 10 do
        if (10%i == 0) then
            print(string.format("%d\n", i));
        end
    end
    

    What if we need to get these numbers in another part of our code? We would just copy and paste the code wherever we need it:

    for i = 1, 10 do
        if (10%i == 0) then
            print(string.format("%d\n", i));
        end
    end
    
    --// we need it here
    
    for i = 1, 10 do
        if (10%i == 0) then
            print(string.format("%d\n", i));
        end
    end
    
    --// we need it here as well
    
    for i = 1, 10 do
        if (10%i == 0) then
            print(string.format("%d\n", i));
        end
    end
    
    --// it goes on
    

    This is messy and tedious! Surely there is a way to simplify this, right? Yes! This is where functions come to a rescue.

    local function printFactors()
        for i = 1, 10 do
            if (10%i == 0) then
                print(string.format("%d\n", i));
            end
        end
    end
    
    --[[
        local function fn() ... end format is syntactic sugar for:
    
        local fn
        fn = function()
            ...
        end
        it's just a local variable that contains a function value.
    
        a global variable that contains a function is written like so:
        
        function fn()
            ...
        end
    
        that is sugar for
    
        fn = function()
            ...
        end
        
        you should not be using globales anyways but it is good info to know
    --]]
    

    Try executing that code above. You will notice that nothing is printing. Why?

    The answer is very simple: you did not call it. You call it simply with identifier().

    In this case you would write

    printFactors()
    

    Now, instead of pasting 50 lines everywhere, you're just pasting one.

    printFactors();
    
    --// i need to call this here
    printFactors();
    
    --// oh and here too
    printFactors();
    
    --// let's not forget here!!!!!
    printFactors();
    

    This is great! We are saving time, readability, and to a small extent we're gaining efficiency. That's the purpose of functions, to modularise and reuse our code! (Within reasonable limits, ofc).

    Parameters and Arguments

    This printFactors function is great, but we don't want only the factors of 10. What if we want the factors of 20? 40? It's going to be a huge pain to write something like:

    local function printFactorsOf20()
        for i = 1, 20 do
            if (20%i == 0) then
                print(string.format("%d\n", i));
            end
        end
    end
    
    local function printFactorsOf40()
        for i = 1, 40 do
            if (40%i == 0) then
                print(string.format("%d\n", i));
            end
        end
    end
    

    So how can we print the factors of any number we like without going in the code and having to change it ourself?

    The answer is simple and easy: parameters and arguments.

    Parameters are a functions input variables. Arguments are a function's input values.

    Let's add some?

    local function printFactors(desiredNumber)
        --// fun fact: parameters are local variables
        for i = 1, desiredNumber do
            if (desiredNumber%i == 0) then
                print(string.format("%d\n", i));
            end
        end
    end
    
    printFactors(32);
    

    You pass the arguments in between the parenthesis when you call the function. So, when printFactors is called, desiredNumber is 32. desiredNumber is the parameter.

    What about if you want to have multiple arguments?

    You just add more parameters, separating each with a comma, like so:

    local function printMod(num1, num2)
        print(num1%num2);
    end
    
    printMod(12, 2);
    

    num2 gets 2 and num1 gets 12.

    You can keep adding parameters. I believe the maximum is 200. You shouldn't have that many anyways.

    Variadic functions

    A variadic function is a function that can take a variable amount of arguments. An example would be the print function.

    You can write something like:

    print(1, 2, 3, 4, 5, 6);
    

    And the arguments will be printed, with a tab separating each value.

    I would like to make it clear that there are no different types of functions. The only differences would be their purposes, how they are being used, and behaviours that describe the function.

    Where the parameters would originally go, you'd instead put an ellipsis ("...").

    local function myFunction(...)
        print(...);
    end
    
    myFunction("Hello world!", "\"\"", "apple pie");
    

    Do note that something like this is also valid:

    local function myFnc(a, b, ...)
        print(...); --// here, i'm only printing additional arguments received
    end
    
    myFnc(7, 5, 6);
    
    --// output should be just 6
    

    For syntactical reasons, ... should be the last or only parameter in between the parenthesis. (a, ..., b) or (..., a, b) for example are not valid.

    Returns

    Functions can also return values. Think of this as getting a "result" from a function call.

    You can do something like:

    local variable = fn(...); --// some arguments you passed to the function
    

    And you can get something useful back, and variable will contain it.

    Let's write that.

    local function mult(num1, num2)
        return num1*num2;
    end
    
    local product = mult(5, 9);
    print(product .. ';'); --> 45;
    

    So, what is happening, is, num1 and num2 are being multiplied (in this case 5 and 9), and the function returns the result. We can collect the result by storing it in a variable, you can even collect them all in a table

    local returns = {fn(...)}

    Functions can also return multiple values.

    An example of a built in function that returns multiple values is string.gsub: it returns the new string as well as how many replacements it made.

    Say you wanted to get the product, quotient, sum, difference, and modulus of 2 numbers in a single function, you could do something like this:

    local function prodQuotSumDiffMod(num1, num2)
        return num1*num2, num1/(rawequal(num2, 0) and 1 or num2), num1 + num2, num1 - num2, num1%(rawequal(num2, 0) and 1 or num2); 
    end
    
    --// collect them manually
    local prod, quot, sum, diff, mod = prodQuotSumDiffMod(25, 2);
    --// print them yourself
    local returns = {prodQuotSumDiffMod(25, 5)} --// all the numbers are in here now
    

    Recursion

    Functions can also be called recursively. This means that the function calls itself within its own definition. A popular example of a recursive function is the factorial function, which can be written like so:

    local function fact(num)
        assert(num >= 0, "number must be positive");
        assert(rawequal(num, math.floor(num + 0.5), "cannot get the factorial of a non-integral number");
    
        if (rawequal(num, 0)) then
            return 1; --// factorial of 0 is 1
        end
        return num*fact(num - 1);
    end
    
    print(fact(5)); --> 120
    

    Before I go into depth with recursion, we must first understand tail calls.

    Proper tail calls

    A tail call is when the last thing a function does is call another function, so the calling function has nothing else to do.

    Take this code snippet as an example:

    local function fn()
        return fn2(...);
    end
    

    After fn calls fn2, there's nothing else for fn to do. In such a situation, the program doesn't need to the function that is calling when the called function has ended. Thus, after this tail call, no information needs to be kept about the calling function in the stack. Some language implementations, like the Lua interpreter, make use of this, and therefore don't use extra space in the stack when doing a tail call. It is said that these implementations support proper tail calls.

    Since due to the fact that proper tail calls don't use stack space, there isn't a limit on how many nested tail calls can be made. For example, this function here can be called with any number as argument, and it will never overflow the stack:

    local function fn(num)
        if (num > 0) then
            return fn(num - 1);
        end
    end
    

    An unclear point when we are using proper tail calls is what is a tail call. For example, this code here is not a tail call:

    local function fn()
        fn2(...);
        return;
    end
    

    The issue with this code snippet is that fn has to discard occasional results from the fn2(...) call before returning. These similar examples are also not tail calls:

    return fn(...)/x; --// needs to divide the first return of `fn` by 1
    return a or fn(...); --// needs to evaluate to 1 return
    return (fn(...)); --// needs to evaluate to 1 return
    

    Only return fn(...) is a tail call. But, the arguments passed could be complex expressions, since Lua evaluates the expressions before the function call.

    This example is still a tail call though:

    return fn(3^(math.random(1, 3)*2), 'a' .. 'b' .. 'c', (21%7 + 2)^4)
    

    Higher order functions, upvalues, and closures

    here is where things get a little tricky

    Again, this is not a type of function, but a name that describes it. A higher order function is a function that takes a function as an argument and/or returns a function.

    An example of a higher order function is pcall: it takes a function to call in protected mode.

    string.gmatch is another example, since it returns a function.

    What are upvalues? What are closures?

    Upvalues

    Upvalues are just external local variables; a local variable declared outside of a function body. You use them all the time and you probably just didn't know it.

    local variable = 5;
    
    local function foo()
        local v2 = 4; --// v2 is not an upvalue; it's declared in the function scope
        --// variable is an upvalue here
    end
    

    Closures

    somewhat tricky

    When a function is defined, and enclosed in another function, the enclosed function is able to access the local variables from the enclosing function (the function it is nested inside). This is known as lexical scoping.

    Sounds obvious, does it? Hint: not. Lexical scoping and first class functions are a powerful concept, and very little programming languages support it.

    Let's use an example, shall we?

    Say you had a list of names, and you want to sort the list from who is the oldest to who is the youngest. You might write something like this:

    local names = {"Phlegethon5778", "incapaxx", "TheeDeathCaster"};
    local ages = {
        Phlegethon5778 = 17,
        incapaxx = 14,
        TheeDeathCaster = 18 --// correct me if ur birthday passed already bby @TheAlphaStigma
    };
    
    table.sort(names, function(name1, name2)
        return ages[name1] > ages[name2];
    end);
    

    And for some reason you need to use a function to do this, probably because you want to reuse the code with different values:

    local function sortByAge(tbl1, tbl2)
        table.sort(tbl1, function(name1, name2)
            return tbl2[name1] > tbl2[name2];
        end);
    end
    

    What is interesting here is that the function passed to table.sort is still able to see tbl1 and tbl2. They are neither local or global variables: they are upvalues.

    Your question might be:

    • "Why is it so interesting?"
      • Functions are first class values. That is why. Consider this code:
    local function start()
        local num = 0 --// start at num
        return function()
            num = num + 1;
            return num;
        end;
    end
    
    local inc = start();
    
    for i = 1, 10 do
        print(inc()); --// increment by 1, 10 times
    end
    
    --// output:
    
    --[[
        /*
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        */
    --]]
    

    The function that is returned uses the num upvalue to keep its counter. Remember that parameters are local variables in the enclosing function, so that is how this works.

    But by the time the returned function is called, num is already out of scope, since the function it was declared in (in this case start) has reached a return (also remember that all functions have an either implicit or explicit return statement).

    Nevertheless, the situation is handled correctly, via that concept of closure.

    In a single sentence, a closure is a function and all that it needs to have access to its upvalues.

    If you notice, when start is called again, a new i variable is declared. So there is a new closure that modifies that new variable.

    Here is an example to demonstrate this:

    local inc = start(0);
    local inc2 = start(0);
    inc(); --// 1
    inc(); --// 2
    print(inc(), inc2()); --// 3    1
    

    Functions and tables

    We have seen and used functions in table fields. An example would be the table.insert function.

    To index a table for a function, and call it, like math.random(), we just write something like:

    local math2 = {
        getReciprocal = function(num)
            return 1/num;
        end,
        sec = function(num)
            return 1/math.cos(num);
        end
    }
    
    --// more or less the same as:
    
    math2.getReciprocal = function(num)
        return 1/num;
    end
    
    math2.sec = function(num)
        return 1/math.cos(num);
    end
    
    --// which is also the same as
    
    function math2.getReciprocal(num)
        return 1/num;
    end
    
    function math2.sec(num)
        return 1/math.cos(num);
    end
    

    And you can call it like how you'd call any other function

    math2.sec(24);
    math2.getReciprocal(4);
    --// do something with the returns
    

    Let's try calling one with a : now.

    math2:getReciprocal(4);
    

    > attempt to perform arithmetic on a table value (local 'num')
    What? An error? Why is this?

    This brings us to the : notation, methods, and a bit of OOP (Object Oriented Programming).

    The : notation

    When you use the : to define a function, there is an implicit self parameter.

    Say you have a function that creates a Pizza.

    You might have something like this...

    local Pizza = {};
    local mt = {__index = Pizza}; --// allows inheritance
    
    function Pizza.new(flavour, sliceAmount)
        local newPizza = {};
        newPizza.Flavour = flavour;
        newPizza.Slices = sliceAmount or 6;
        setmetatable(newPizza, mt);
        return newPizza;
    end
    
    function Pizza:EatSlice()
        if (self.Slices < 1) then
            return;
        end
    
        self.Slices = self.Slices - 1;
    end
    --// same as VVVVV
    --[[
    function Pizza.EatSlice(self)
        if (self.Slices < 1) then
            return;
        end
    
        self.Slices = self.Slices - 1
    end
    --]]
    

    Focus on the :EatSlice function. What is this self? We haven't defined it anywhere?

    It is an implicit parameter when using the : notation to define the function. If we want to eat a slice out of the pizza, we can of course call the function like so:

    local myPizza = Pizza.new("Pepperoni", 8);
    myPizza:EatSlice();
    

    When you call a function using the : notation, you are passing myPizza (in this case) as an argument, and this is implicit, sort of as if you were telling the object to behave in a certain way. This is the self. The object you're calling the function on.

    myPizza:EatSlice() is syntactic sugar for myPizza.EatSlice(myPizza). Of course, if you use the . notation, you can pass another object, and it would be that self. Here is an example of what I mean:

    local pepperoniPizza = Pizza.new("Pepperoni", 8);
    local veggiePizza = Pizza.new("Vegetarian", 6);
    pepperoniPizza.EatSlice(veggiePizza);
    print(veggiePizza.Slices); --> 5
    

    It's all the same function!

    Please note

    I did not cover absolutely everything about OOP. This only scratches the surface. I recommend doing some research on OOP if you are interested.

    Conclusion

    This was a lengthy tutorial, and it didn't cover a couple of these subjects in the detail they should be covered in. I strongly recommend researching some tutorials on OOP concepts, and functions in general. I'll try and edit this post if I find new information. Hope this helped clear things up for you, the reader, and let me know if you have any questions, as well as tips.

    posted in Tutorials
  • RE: Part 1: All about tables

    I suggest for part 2 you cover metatables and metamethods and stuff cuz that is where things get juicy

    also don't mind i'm gonna copy you and make a tutorial on functions

    posted in Tutorials
  • RE: Part 1: All about tables

    This is a really nice tutorial!

    Apart from this mistake everything else is fine

    local y = {
         [21.2] = "happy";
         --# I don't recommend using keys with over 1-2 decimal places. Because of the way computers store variables, I'm pretty sure there could be some bugs caused.
         [-32] = "joe"; --# I said we would get around to this
         [true] = 1234567890;
         [{1, 2, 3}] = "Oh my, we just indexed with a table.";
    } --# as you can see, these "associative arrays" are extremely flexible
    
    print(y[21.2]) --# output --> happy
    print(y[{1, 2, 3}]) -- output --> Oh my, we just indexed with a table.
    --# you can test the rest if you like
    

    Look at print(y[{1, 2, 3}]).

    This is going to evaluate to nil. Remember that tables are passed by reference, even if the table that is the key in the construction of the table is identical in values to the table that you are using as the index, it is not the same exact table.

    This is also true for threads, userdatas, and functions.

    posted in Tutorials
  • RE: Libraries+, an extension to the libraries

    16/04/19:

    bypassMetamethod has been removed, documentation for it has been as well. This function was made just for show, and now it goes bye bye

    posted in Cool Creations
  • RE: Libraries+, an extension to the libraries

    Another function!

    void setChildren(Instance instance, table children)

    It's pretty much the opposite of :GetChildren. It takes a table of instances, and parents each instance to instance.

    Here is an example:

    local children = game:GetService("ServerStorage").Maps:GetChildren();
    --// you might want to put all your game's maps into the workspace
    LibrariesPLUS.misc.setChildren(game:GetService("Workspace"), children);
    
    posted in Cool Creations
  • RE: The new editor is pretty buggy

    @Phlegethon5778 read before replying:

    What is needed is "documentation" on the site, especially for the stack edit.

    posted in Scripting Helpers Discussion
  • RE: The new editor is pretty buggy

    @SteamG0D nope

    ask eryn, but to be honest posting pictures without needing to link them with gyazo or something was a must.

    I don't think strike through text was a thing before (it is now). I never saw it.

    Most of the pages are outdated now. What is needed is "documentation" on the site, especially for the stack edit.

    posted in Scripting Helpers Discussion
  • RE: The new editor is pretty buggy

    @SteamG0D The only issues I've had now are spacing issues. Not exactly indenting, but sometimes my expression gets waaay too spaced out

    if    (expr)    then
    
    end
    
    posted in Scripting Helpers Discussion

Looks like your connection to Scripting Helpers was lost, please wait while we try to reconnect.