ExecutionByFork
ExecutionByFork

Reputation: 338

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

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?

Upvotes: 4

Views: 18269

Answers (2)

Jason Goemaat
Jason Goemaat

Reputation: 29214

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"})

Upvotes: 3

Paul Kulchenko
Paul Kulchenko

Reputation: 26764

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)

Upvotes: 1

Related Questions