Calling other resources (exports)
Every server resource runs in its own Lua state. A global variable in one resource does not exist in another. With modkit.exports a resource offers functions by name, and other resources call them, the way FiveM does it.
Offer a function
resources/bank/server.lua
local function balance(steamId)
return modkit.storage.get("balance:" .. steamId, 0)
end
modkit.exports.register("balance", balance)
modkit.exports.register("add", function(steamId, amount, reason)
if type(steamId) ~= "string" or type(amount) ~= "number" then return false, "bad arguments" end
local total = balance(steamId) + amount
if total < 0 then return false, "not enough money" end
modkit.storage.set("balance:" .. steamId, total)
modkit.log.info(steamId .. " " .. amount .. " (" .. tostring(reason) .. ")")
return true, total
end)
Call it
resources/shop/server.lua
local PRICES = { Stegosaurus = 500, Tyrannosaurus = 1500 }
modkit.commands.add("buy", function(player, species)
local price = PRICES[species or ""]
if not price then player:message("usage: /buy Stegosaurus or /buy Tyrannosaurus") return end
local ok, paid, info = pcall(modkit.exports.bank.add, player.steamId, -price, "bought " .. species)
if not ok then player:message("The bank is closed.") return end
if not paid then player:message("Payment failed: " .. info) return end
local slot, why = modkit.garage.give(player, { species = species, growth = 1, fill = true }, { name = "Bought " .. species })
if not slot then
modkit.exports.bank.add(player.steamId, price, "refund")
player:message("Your garage is full, you got your money back.")
return
end
player:message("Bought. Spawn as " .. species .. " and use /restore " .. slot.id)
end)
modkit.events.add("playerDied", function(player, killer)
if killer and killer.steamId then modkit.exports.call("bank", "add", killer.steamId, 25, "kill") end
end)
modkit.exports.bank.add(...) and modkit.exports.call("bank", "add", ...) are the same call.
server.cfg has to load the provider first.
load bank
load shop
Rules
Values are copied. Arguments and return values travel as copies. nil, booleans, numbers, strings and tables of those work. A table you pass is not shared, the other side changing it does not change yours.
No functions. A function as an argument raises an error. A function inside a table is dropped without a message. If the other resource should call you back, export a second function, or use a local event with modkit.events.call, which reaches every server resource.
Player tables keep their methods. Pass a player table and the other side can call player:message(...) on it.
Errors come back as errors. A missing resource, a missing export and an error inside the exported function all raise an error at the caller. The text names the resource and the export. Wrap the call in pcall when the other resource is optional, as the shop does above.
Validate what you get. An export is a public function. Check the argument types like bank.add does, because a typo in another resource should not corrupt your data.
Load order matters once. A call made while the file is loading needs the other resource to be loaded already. Calls made later, from events, commands and timers, do not care about the order. modkit.exports.list("bank") returns nil while bank is not running.
Server only. Client resources have no exports. On the client, local events with modkit.events.call do the same job.