More than one return value for Lua exports

I recently created a small resource that is using exports for callbacks. This has been created from a built-in callback in another resource of mine.

As we all know, lua functions can have several return values:

function TestReturn()
    return data1, data2, data3
end

local a, b, c = TestReturn()

But this doesn’t work for exports. I am guessing this is because of compatibility with C# and js exports?

My suggestion would be to remove this restriction for Lua entirely and in C# instead something like an object array containing the values. (I have never worked with js before, but I guess something similar would be possible there as well)

(related: Lua exports only allowing a single return value )

There should be no issue with exports or function reference calls supporting multiple return values. They would just be treated as arrays in the C# and JS script runtimes.

An attempted fix was initially PRd in: fix(scripting/lua): support exported MRVs by lze3 · Pull Request #681 · citizenfx/fivem · GitHub. However, I completely forget all details of that discussion and what bubble had envisioned.

Hmm… Well I could just make another PR for this, though it probably wouldn’t be a lot different from the one you linked.

Maybe we can get @deterministic_bubble in on this?

Here’s an outline to a solution you may run with.

-- Handle an export with multiple return values.
local function exportProcessResult(resource, k, status, ...)
	if not status then
		local result = tostring(select(1, ...))
		error('An error happened while calling export ' .. k .. ' of resource ' .. resource .. ' (' .. result .. '), see above for details')
	end
	return ...
end

setmetatable(exports, {
	__index = function(t, k)
		local resource = k

		return setmetatable({}, {
			__index = function(t, k)
				lazyEventHandler()

				if not exportsCallbackCache[resource] then
					exportsCallbackCache[resource] = {}
				end

				if not exportsCallbackCache[resource][k] then
					TriggerEvent(getExportEventName(resource, k), function(exportData)
						exportsCallbackCache[resource][k] = exportData
					end)

					if not exportsCallbackCache[resource][k] then
						error('No such export ' .. k .. ' in resource ' .. resource)
					end
				end

				return function(self, ...) -- TAILCALL
					return exportProcessResult(resource, k, pcall(exportsCallbackCache[resource][k], ...))
				end
			end,

			__newindex = function(t, k, v)
				error('cannot set values on an export resource')
			end
		})
	end,

	__newindex = function(t, k, v)
		error('cannot set values on exports')
	end,

	__call = function(t, exportName, func)
		AddEventHandler(getExportEventName(GetCurrentResourceName(), exportName), function(setCB)
			setCB(func)
		end)
	end
})

That indeed seems close enough and won’t cause GC load for a temporary table, indeed.

In between working on my other scripts I was trying to setup the build environments for FiveM to see, if my changes will work before issueing a PR.

But I am having a few problems setting that up.

Where would be the best place to ask about that?

A topic here or perhaps the Matrix chat, which is lower-traffic and generally used for more project tech-related discussions.

1 Like