M MY Multiplayer: Wiki

Módulos e import

Como organizar o código Lua de um recurso em módulos dentro de modules/ e reaproveitá-los no MY Multiplayer (MYMP) com import.

No MY Multiplayer (MYMP), um módulo é um arquivo .lua dentro da pasta modules/ de um recurso. Ele não precisa ser declarado no meta.xml: o servidor encontra todos os arquivos automaticamente, compila cada um a cada início do recurso e envia os módulos compartilhados e de cliente junto com ele.

cart.luasó no cliente · nome ui/cart
main.luasó no servidor · nome main
counter.luacompartilhado · nome counter
money.luacompartilhado · nome money

Onde cada módulo roda

  • modules/server/: executa apenas no servidor e nunca é enviado aos jogadores.
  • modules/client/: executa apenas no cliente.
  • Qualquer outro caminho dentro de modules/: compartilhado (disponível nos dois lados).

Nome e resolução de um módulo

O nome do módulo é o seu caminho relativo dentro de modules/, sem a extensão .lua e sem o prefixo server/ ou client/.

Arquivo Nome do módulo Lado
modules/money.lua money Compartilhado
modules/server/main.lua main Servidor
modules/client/ui/cart.lua ui/cart Cliente
  • Precedência por lado: se um lado tiver um módulo próprio com o mesmo nome de um módulo compartilhado, o módulo específico daquele lado vence.
  • Módulo padrão (main): chamar import "@shop" equivale a import "@shop/main".

Compartilhar código devolvendo uma tabela

A forma mais explícita de expor funcionalidades é agrupar funções e valores em uma tabela local e devolvê-la com return. Quem chama import recebe essa tabela.

modules/money.lua:

local money = {}

function money.format(value)
    return "$" .. value
end

return money

Uso no script importador:

local money = import "@shop/money"

print(money.format(25))

Variáveis declaradas com local permanecem privadas ao módulo. Quando um módulo não executa return, import devolve true.

Compartilhar código por variáveis globais

As variáveis globais criadas pelo módulo são entregues ao ambiente de quem o importou, sem vazar para outros recursos: import transfere apenas as globais definidas pelo módulo.

modules/money.lua:

function formatMoney(value)
    return "$" .. value
end

TAX = 0.1

Uso no script importador:

import "@shop/money"

print(formatMoney(25), TAX)

Escolher quais globais importar

Passando uma lista de nomes antes do identificador do módulo, apenas as globais listadas são trazidas. Se o módulo não definir algum dos nomes pedidos, import lança um erro.

import { "formatMoney" } "@shop/money"

print(formatMoney(25))
print(TAX)

Nesse exemplo, formatMoney fica disponível, mas TAX não: o segundo print exibe nil.

Execução única e estado por recurso

Um módulo executa apenas uma vez dentro de cada recurso que o importa. Importá-lo novamente no mesmo recurso devolve o mesmo resultado em cache, de modo que o estado interno é compartilhado entre scripts daquele recurso.

modules/counter.lua:

local count = 0
local counter = {}

function counter.add()
    count = count + 1
    return count
end

return counter

Uso no mesmo recurso:

local a = import "@shop/counter"
local b = import "@shop/counter"

print(a == b)
print(a.add())
print(b.add())

Isso imprime true, 1 e 2, pois a e b apontam para a mesma tabela. Se outro recurso importar @shop/counter, o módulo será executado novamente no estado daquele outro recurso, com sua própria contagem independente.

Como o módulo roda dentro do estado de quem o importou, variáveis globais como resourceRoot, além dos eventos e timers criados por ele, pertencem ao recurso importador. Este módulo, quando importado pelo recurso shop, registra o handler no resourceRoot de shop:

addEvent("onShopBuy", true)

addEventHandler("onShopBuy", resourceRoot, function(item)
    print(getPlayerName(client) .. " comprou " .. item)
end)

Importar de outro recurso

local accounts = import "@accounts/api"

addEventHandler("onPlayerJoin", root, function()
    accounts.load(source)
end)
  • No servidor: se o recurso accounts não estiver em execução, import o inicia automaticamente antes de carregar o módulo.
  • No cliente: o recurso alvo já precisa estar carregado naquele cliente.

Mensagens de erro do import

Mensagem Causa
import: "shop/money" is not a module name such as "@resource" or "@resource/module" O identificador do módulo não começa com @.
import: there is no resource called shop No servidor, não existe recurso com esse nome.
import: shop is not loaded on this client No cliente, o recurso ainda não foi carregado.
import: shop has no module "money" O recurso não possui um módulo com esse nome.
import: @shop/main is client-only O módulo existe apenas no outro lado (neste caso, só no cliente).
import: @shop/money does not compile: … O arquivo do módulo contém erro de sintaxe Lua.
import: @shop/money is imported again while it runs (an import cycle) Ciclo de importação: dois módulos importam um ao outro ao iniciar.
import: @shop/money defines no "TAXES" Um nome pedido na lista { … } não foi definido como global.