Lua: How do I execute a callback passed from a parameter?

10,708

Solution 1

Functions can be variables in lua, but they are just like any other variable.

I don't know what is wrong with your example, it seems like you have it exactly right. Just entering your code works (try it here).

Update

It appears that FiveM and essentialmode are client/server and running in two different execution environments. They probably serialize data for the communication between the client and server, but passing a function wouldn't work.

There may be something you can do by using loadstring()... Here's an example you can run at jdoodle:

function sayHi(name)
  print("Hello, "..tostring(name).."!")
end

-- create string representation of the function
local functionDef = string.dump(sayHi)

-- convert back to a function
local func = loadstring(functionDef)
func('Jason')

-- or use a string directly, just body, no arguments so
-- we 'return' a function and call it with () after loadstring 
-- to get the returned function - [[ ]] is just syntax for a
-- multiline string
local func2 = loadstring([[
        return function(name) 
          print("Hi there, "..tostring(name).."!")
        end
    ]])()
func2('Jason')

String.dump serializes an existing function as a string and loadstring converts it back to a function. You could pass this string between the client and server, but it would execute in whatever environment it was executed on. You can also pass a function body, but to get arguments you have to return a function with arguments in that body and call it to get the return value to use that function.

If GiveWeaponToPed and SetPedComponentVariation are executed on the client then you could send those commands in place of the 'callback' argument and use loadstring in the client...

client

RegisterNetEvent("parachute:callback")
AddEventHandler('parachute:callback', function(callback)
    print("callback...")
    loadstring(callback)()
end)

server

codestring = [[
    GiveWeaponToPed(GetPlayerPed(-1), GetHashKey("GADGET_PARACHUTE"), 150, true, true)
    SetPedComponentVariation(GetPlayerPed(-1), 5, 1, 0, 0)
]]

TriggerEvent('es:addCommand', 'callback', function(source, args, user)
    TriggerClientEvent("parachute:callback", source, codestring)
end, {help = "TEST"})

Solution 2

It will work exactly as you described it. You may want to add local in front of your callback function, but other than that, you should be good. Here is a working example:

local callback = function ()
    print("here")
end

function caller(callback)
    callback()
end

-- run caller function
caller(callback)
Share:
10,708
ExecutionByFork
Author by

ExecutionByFork

Updated on June 04, 2022

Comments

  • ExecutionByFork
    ExecutionByFork over 1 year

    I have searched for an answer to this question without luck. I have only found examples of people passing a callback into a caller function, but haven't seen how the caller actually executes the callback function code internally. If someone could provide me an example and explanation on this it would be much appreciated.

    I understand how callbacks work from a functional perspective, I've used them in other languages. I just can't figure out the syntax of how to do it right in lua. I am either executing the callback parameter wrong or passing it into the caller function wrong.

    Here is some psudocode of what I am trying to do, just a simple example so I can see it work. (I am a visual learner)

    callback = function ()
        ... do stuff ...
    end
    
    function caller(callback)
        callback()
    end
    
    -- run caller function
    caller(callback)
    

    EDIT: I have gotten a few answers saying that my example above works, and it seems like it actually does. Thank you for this, but I suppose I should get more specific considering that my actual code isn't working despite the right syntax.

    I am doing some coding for a FiveM server which utilizes essentialmode. I am attempting to use a callback in a script to clean the code up, but it doesn't seem to be firing the callback function. Here are the snippets...

    The client script:

    RegisterNetEvent("parachute:callback")
    AddEventHandler('parachute:callback', function(callback)
        print("callback...")
        callback()
    end)
    

    The server script:

    callback = function ()
        print("callback fired")
        GiveWeaponToPed(GetPlayerPed(-1), GetHashKey("GADGET_PARACHUTE"), 150, true, true)
        SetPedComponentVariation(GetPlayerPed(-1), 5, 1, 0, 0)
    end
    
    TriggerEvent('es:addCommand', 'callback', function(source, args, user)
        TriggerClientEvent("parachute:callback", source, callback)
    end, {help = "TEST"})
    

    In the above, the print("callback...") function executes but the callback() function does not. I can tell due to the print statements that I added.

    For more context to those of you not familiar with essential mode or FiveM scripting, there is a client loaded script and a server loaded script. In the server, TriggerEvent('es:addCommand', ... ) adds a command you can execute using /<cmd name>. TriggerClientEvent() kicks off an event on the client via a listener created by RegisterNetEvent() and AddEventHandler(). As you can see you pass callbacks to these functions to tell them what to execute when the event is triggered. The callback function itself simply gives a parachute to my character. No parachute is given when I type /callback however.

    What is going wrong if it's not the syntax like I originally thought?

    • Jason Goemaat
      Jason Goemaat over 5 years
      Just a stab, but are you sure the arguments are correct? Can you do a dump of the args along with your print("callback...")? Maybe add a print('callback type: '..type(callback)) and if it's a table for k,v in pairs(callback) do print('callback["'..tostring(key).."]: '..tostring(v)) end
    • Jason Goemaat
      Jason Goemaat over 5 years
      Not sure how essentialmode works, but it looks like you're declaring a callback function on the server and passing it to the client with the event so that the client can call the function on the server. I doubt that will work, I assume they are isolated enironments.
    • ExecutionByFork
      ExecutionByFork over 5 years
      Yes, I believe this is why. I overlooked the fact that the server and client had two different address spaces. If you edit your answer to add this in (for completeness) I'll accept yours as the correct answer
  • Tom Blodget
    Tom Blodget over 5 years
    To clarify: There are two kinds of variables, global and local (which includes loop variables and parameters, which can include self). And, there are several types of values, including function. Saying "Functions can be variables" or "global…function" sounds odd. Your reasoning is sound though.
  • ExecutionByFork
    ExecutionByFork over 5 years
    Thank you for the feedback, I've updated my question
  • ExecutionByFork
    ExecutionByFork over 5 years
    Thank you for the feedback, I've updated my question