Pular para o conteúdo

Resources e mtaxmanifest.lua

Um resource é uma pasta dentro de resources/ contendo um arquivo mtaxmanifest.lua. Ele é a unidade que o servidor inicia, para e reinicia — e cada um roda em sua própria VM Lua, isolada das demais.

  • Directoryresources/
    • Directorymeu-resource/
      • mtaxmanifest.lua obrigatório
      • Directoryserver/
        • main.lua
      • Directoryclient/
        • main.lua
      • Directoryassets/
        • logo.png

O nome da pasta é o nome pelo qual você inicia o resource no console e no server.cfg.

Uma pasta cujo nome está entre colchetes é uma categoria: ela não é um resource, apenas agrupa outros.

  • Directoryresources/
    • Directory“[gameplay]”/
      • Directoryspawn/
        • mtaxmanifest.lua
      • Directoryrespawn/
        • mtaxmanifest.lua

Categorias podem ser aninhadas. No console, usar o nome da categoria aplica o comando a todos os resources dentro dela.

mtaxmanifest.lua é um script Lua que apenas declara variáveis — o servidor lê os valores após executá-lo.

resource_name = "meu-resource"
resource_version = "1.0.0"
resource_author = "Seu Nome"
server_files = {
"server/main.lua",
}
client_files = {
"client/main.lua",
}
files = {
"assets/logo.png",
}
VariávelTipoDescrição
resource_nametextoNome do resource.
resource_versiontextoVersão, livre (ex.: 1.0.0).
resource_authortextoAutor ou equipe responsável.

Os três são expostos aos scripts por getResourceInfo.

VariávelAceitaOnde roda
server_filesapenas .luaSó no servidor. Nunca é enviado ao cliente.
client_filesapenas .luaBaixado e executado no cliente.
shared_filesapenas .luaExecutado nos dois lados.
filesqualquer coisa exceto .luaAssets baixados pelo cliente (imagens, fontes, modelos, sons).
client_configsqualquer arquivo exceto .luaArquivos XML de configuração que o cliente lê com getResourceConfig (o <config type="client"> do MTA). São baixados como os de files; não é preciso listá-los duas vezes.
map_filesqualquer arquivo exceto .luaInterpretado pelo servidor como XML de mapa; nunca é enviado ao cliente. A extensão convencional é .map. Uma entrada é tanto uma string "dir/arquivo.map" quanto uma tabela { src = "dir/arquivo.map", dimension = N, interior = N } — o dimension e o interior informados ali viram os padrões de todos os elementos daquele mapa.

Uma entrada também pode apontar para um arquivo dentro de outro resource — veja Pegando emprestado de outro resource.

Os scripts são lidos como UTF-8. Quando um arquivo está salvo em outra codificação, o servidor o converte para UTF-8 — tanto a cópia que ele mesmo executa quanto a cópia que compila para os jogadores — e, para os scripts que executa, imprime um aviso pedindo que você salve o arquivo de novo como UTF-8:

O arquivo está emO que acontece
UTF-8 com BOMO BOM é removido.
UTF-16 ou UTF-32 com BOMConvertido para UTF-8. Um arquivo que não decodifica falha ao carregar.
UTF-8 inválido (ANSI)Lido como Windows-1252 e convertido para UTF-8, então os literais de string acentuados mantêm seus caracteres nos dois lados.

Todo caminho é relativo à pasta do resource e precisa:

  • usar / como separador — \ é rejeitado;
  • não começar com /;
  • não conter .., . ou :, com a única exceção do :resource/ inicial de uma entrada emprestada.

Além da validação textual, o servidor resolve o caminho canônico e rejeita qualquer entrada explícita que termine fora da raiz do resource — inclusive por meio de um link simbólico.

As listas aceitam curingas no estilo FiveM:

PadrãoSignificado
*Qualquer coisa dentro de um segmento do caminho.
**Qualquer coisa, atravessando pastas.
client_files = {
"client/*.lua", -- só os .lua direto em client/
"client/**/*.lua", -- todos os .lua em client/ e subpastas
}
files = {
"assets/**/*.png",
"fonts/*.ttf",
}

Detalhes que importam:

  • A comparação não diferencia maiúsculas de minúsculas.
  • O resultado é ordenado alfabeticamente, então a ordem de carregamento dos scripts é estável.
  • Caminhos repetidos por padrões sobrepostos (*.png e **/*.png) são deduplicados, mantendo a primeira ocorrência.
  • Um glob que não encontra nada expande para nada — isso não é erro.
  • O próprio mtaxmanifest.lua nunca é incluído por um glob.
  • A expansão não segue links simbólicos; arquivos e diretórios ligados são ignorados pelo glob.

Uma entrada que começa com : aponta para um arquivo que mora na pasta de outro resource:

shared_files = {
":core/lib/class.lua", -- o arquivo pertence ao resource "core"
"shared/util.lua", -- este é nosso
}
files = {
":brand/assets/**/*.png", -- todos os png que o resource "brand" tem em assets/
}

O formato é ":<resource>/<caminho>" — o nome do resource, depois um caminho relativo à pasta dele. É aceito em server_files, client_files, shared_files e files.

Declarar já é a permissão inteira. O outro resource não precisa autorizar, não precisa estar rodando e não é avisado — exatamente como no FiveM. Ele só precisa existir.

  • A separação script/asset vale também atravessando a fronteira: um .lua emprestado vai em uma lista de scripts, um asset emprestado vai em files.
  • O caminho depois do nome do resource segue as mesmas regras de caminho e aceita os mesmos globs.
  • Uma entrada explícita mantém o lugar dela na lista, então a ordem de carregamento é exatamente a escrita. Um glob emprestado expande no lugar, em ordem alfabética.
  • Duplicatas são removidas, mantendo a primeira ocorrência.
  • Um script emprestado roda na sua VM Lua, não na do dono. getThisResource() dentro dele é o seu resource, e ele enxerga as globais e o ambiente que o seu resource montou.
  • Os erros apontam para o dono: o chunk se chama @<dono>/<caminho>, então o stack trace nomeia o arquivo onde ele realmente mora.
  • Pegar emprestado não inicia, para nem reinicia o outro resource, e reiniciar o dono não recarrega a cópia que o seu resource está rodando.
  • map_files não pode pegar emprestado. Mapas são lidos direto da sua própria pasta, então a entrada é rejeitada na inicialização em vez de silenciosamente resolver para nada.
  • Um arquivo emprestado nunca chega ao armazenamento da NUI. nui://<resource>/ endereça os arquivos de um resource só, então um ui_page não pode apontar para um caminho emprestado e a página não consegue carregar um.

A lista inteira é resolvida depois que todas as pastas de resource foram varridas. Se algo estiver errado, o resource que pediu emprestado é rejeitado e o motivo aparece no console:

MensagemCausa
malformed cross-resource entryFalta a /, ou o nome do resource ou o caminho está vazio.
names a resource that does not existNenhum resource com aquele nome foi encontrado.
borrowed file does not existO caminho explícito não é um arquivo na pasta do dono. Um glob que não encontra nada continua sendo válido.
map_files cannot borrow from another resourceUma entrada com : em map_files.
client_configs cannot borrow from another resourceUma entrada com : em client_configs.

Declarar ":outro/<caminho>" também dá aos seus scripts acesso de leitura àquele caminho por fileOpen, loadfile e as funções path*. Só a leitura atravessa a fronteira, e só nos caminhos que você declarou. Veja Alcançando outro resource.

files = {
"ui/index.html",
"ui/style.css",
}
ui_page = "ui/index.html"

ui_page aponta o HTML de entrada da interface do resource. Ele precisa estar listado em files e não pode ser um .lua. Errando qualquer uma das duas, o resource é recusado com o motivo impresso no console.

Tudo o que a página carrega — folhas de estilo, scripts, imagens, fontes — também precisa estar em files: essa lista é exatamente o que o jogador baixa. O cliente então serve a página da memória em nui://<resource>/, uma origem por resource.

Dali em diante o script conversa com a página usando sendNuiMessage e registerNuiCallback. Veja o guia de NUI para o modelo inteiro, e as funções de NUI para a referência.

loadscreen = true

Marca o resource como tela de carregamento: ele é baixado antes de todos os outros. É apenas um sinalizador true/false — o HTML da tela vai em ui_page.

A página de carregamento recebe o progresso do download como uma message com type = "loadProgress", carregando pending e total. Ela é aposentada quando o download inicial termina; reiniciar o resource depois não traz ela de volta.

loadscreen = true
loadscreen_manual_shutdown = true

Com loadscreen_manual_shutdown = true a tela não é encerrada quando o download termina. Ela fica no ar até um script de cliente chamar o shutdownLoadingScreen — que é como você roda uma introdução, ou espera os seus próprios dados ficarem prontos, antes de deixar o jogador entrar.

É um sinalizador true/false, e só faz algo junto de loadscreen = true. Declarado sozinho, o resource ainda sobe, mas o servidor registra um aviso de que o sinalizador não tem efeito.

exports = {
"getPlayerMoney",
"setPlayerMoney",
}

Declara quais funções o resource oferece a outros resources. Cada nome precisa ser um identificador Lua simples: começar com letra ou _ e continuar apenas com letras, números ou _. Os nomes declarados aparecem em getResourceExportedFunctions e podem ser chamados com call.

Um export só pode ser chamado no mesmo lado em que sua função existe: servidor chama servidor e cliente chama cliente. Com a tabela global exports, use sempre : para que o proxy consuma o argumento implícito corretamente:

local saldo = exports.banco:getSaldo(jogador)

Resource parado, função não declarada ou falha na chamada devolve false e registra o motivo no debug.

resource_info = {
description = "Sistema de contas",
type = "gameplay",
}

Pares de texto livres, lidos por getResourceInfo. As chaves name, version e author são preenchidas automaticamente a partir de resource_name, resource_version e resource_author quando você não as declara aqui.

resource_name = "meu-resource"
resource_version = "1.2.0"
resource_author = "Minha Equipe"
resource_info = {
description = "Exemplo de resource completo",
}
server_files = {
"server/config.lua",
"server/main.lua",
}
client_files = {
"client/**/*.lua",
}
shared_files = {
"shared/util.lua",
}
files = {
"ui/index.html",
"assets/**/*.png",
}
map_files = {
"maps/base.map",
{ src = "maps/escritorio.map", dimension = 5, interior = 3 },
}
ui_page = "ui/index.html"
exports = {
"getSomething",
}