Modules and import
How to split a resource's Lua code into modules under modules/ and reuse them across MY Multiplayer (MYMP) resources using import.
In MY Multiplayer (MYMP), a module is a .lua file inside a resource's modules/ folder. It does not need to be listed in meta.xml: the server discovers every module automatically, compiles each one whenever the resource starts and sends shared and client modules to players along with the resource.
Where each module runs
modules/server/: runs on the server only and is never sent to players.modules/client/: runs on the client only.- Anywhere else inside
modules/: shared (available on both sides).
Module naming and resolution
A module's name is its relative path inside modules/, without .lua and without a leading server/ or client/.
| File | Module name | Side |
|---|---|---|
modules/money.lua |
money |
Shared |
modules/server/main.lua |
main |
Server |
modules/client/ui/cart.lua |
ui/cart |
Client |
- Side precedence: if a side has its own module with the same name as a shared one, that side's module wins.
- Default
mainmodule:import "@shop"is shorthand forimport "@shop/main".
Sharing code by returning a table
The clearest pattern is to gather exported functions and values into a local table and return it. The caller receives that table from import.
modules/money.lua:
local money = {}
function money.format(value)
return "$" .. value
end
return moneyImporting script:
local money = import "@shop/money"
print(money.format(25))Anything declared local inside the module stays private to it. A module with no return makes import return true.
Sharing code through globals
Globals defined by a module are copied into the importing resource's environment without leaking anywhere else: import hands over only the globals the module defined.
modules/money.lua:
function formatMoney(value)
return "$" .. value
end
TAX = 0.1Importing script:
import "@shop/money"
print(formatMoney(25), TAX)Selecting specific globals
Passing a table of names before the module path imports only those listed globals. If the module does not define one of them, import raises an error.
import { "formatMoney" } "@shop/money"
print(formatMoney(25))
print(TAX)Here formatMoney is imported while TAX is not: the second print outputs nil.
Single execution per importing resource
A module runs only once inside each resource that imports it. Importing it again within the same resource returns the cached result, so state kept inside the module is shared across that resource's scripts.
modules/counter.lua:
local count = 0
local counter = {}
function counter.add()
count = count + 1
return count
end
return counterWithin the same resource:
local a = import "@shop/counter"
local b = import "@shop/counter"
print(a == b)
print(a.add())
print(b.add())This prints true, 1 and 2 because a and b reference the same table. Another resource that imports @shop/counter runs the module in its own Lua state and keeps an independent count.
Because a module runs inside the importer's state, global variables such as resourceRoot, as well as any events and timers the module creates, belong to that importing resource. When imported by shop, this module attaches the handler to shop's resourceRoot:
addEvent("onShopBuy", true)
addEventHandler("onShopBuy", resourceRoot, function(item)
print(getPlayerName(client) .. " bought " .. item)
end)Importing from another resource
local accounts = import "@accounts/api"
addEventHandler("onPlayerJoin", root, function()
accounts.load(source)
end)- On the server: if the
accountsresource is not running,importstarts it first. - On the client: the target resource must already be loaded on that client.
Errors raised by import
| Message | Cause |
|---|---|
import: "shop/money" is not a module name such as "@resource" or "@resource/module" |
The module specifier does not start with @. |
import: there is no resource called shop |
On the server, no resource exists with that name. |
import: shop is not loaded on this client |
On a client, the target resource has not been loaded yet. |
import: shop has no module "money" |
The resource has no module with that name. |
import: @shop/main is client-only |
The module only exists on the other side (here, client-only). |
import: @shop/money does not compile: … |
The module file has a Lua syntax error. |
import: @shop/money is imported again while it runs (an import cycle) |
Circular import: two modules import each other during startup. |
import: @shop/money defines no "TAXES" |
A name in the { … } list is not a global defined by the file. |