mri-QboxResourcesMRI Qwhitelist

mri_Qwhitelist — Manual

Whitelist por exame de cidadania: o jogador entra isolado em um routing bucket próprio e só é liberado para o mundo depois de acertar o percentual mínimo de um questionário de múltipla escolha.


Sumário

  1. Dependências
  2. Instalação
  3. Configuração
  4. Fluxo do jogador
  5. Perguntas do exame
  6. Perguntas pré-exame e webhook do Discord
  7. Menu de gerenciamento
  8. Banco de dados
  9. Integrações
  10. Entrypoints para outros recursos
  11. Estrutura de arquivos

Dependências

RecursoObrigatórioObservação
qbx_coreSimGetPlayer, GetPlayerByCitizenId, SetPlayerBucket
ox_libSimDiálogos (inputDialog, alertDialog), context menus, zonas, callbacks e notificações
oxmysqlSimPersistência da whitelist e da configuração
ox_targetNãoApenas se Config.Interaction.Type = "target"
mri_QboxNãoRegistra o menu de gerenciamento e fornece GetRayCoords para capturar coordenadas

O fxmanifest.lua também exige /server:4500 como versão mínima do FXServer.


Instalação

  1. Copie a pasta mri_Qwhitelist para resources/.
  2. Copie server/config.sample.lua para server/config.lua e ajuste as opções. Renomeie ou remova o config.sample.lua depois de copiar — o fxmanifest carrega server/*.lua, então os dois arquivos entram no bundle e a tabela Config do sample pode sobrescrever a sua.
  3. Adicione ao server.cfg:
    ensure ox_lib
    ensure oxmysql
    ensure qbx_core
    ensure mri_Qwhitelist
  4. As tabelas MySQL são criadas automaticamente no primeiro start — não há .sql para importar.

Configuração

Arquivo: server/config.lua (modelo em server/config.sample.lua).

Importante: depois do primeiro start, o recurso passa a ler a configuração da tabela mri_qwhitelistcfg. Se essa tabela tiver um registro, ele sobrescreve o arquivo Lua. Qualquer alteração feita pelo menu de gerenciamento é gravada lá.

CampoTipoObrigatórioDescrição
Config.EnabledboolSimfalse desativa a whitelist — CheckCitizenship retorna true para todos
Config.DebugboolNãoDesenha a citizenZone no mundo (passado como debug para a zona do ox_lib)
Config.PercentnumberSimPercentual mínimo de acertos para liberar o jogador
Config.TimerTimeoutnumber (ms)NãoIntervalo do loop que verifica se o jogador saiu da zona. Padrão interno: 5000
Config.loadNotifystringNãoNotificação exibida ao carregar sem cidadania
Config.escapeNotifystringNãoNotificação exibida ao tentar sair da zona do exame
Config.StartExamLabelstringNãoRótulo genérico do início do exame
Config.StartExamHeaderstringNãoTítulo do diálogo de confirmação antes do exame
Config.StartExamContentstringNãoTexto do diálogo de confirmação antes do exame
Config.SuccessHeaderstringNãoTítulo do diálogo de aprovação
Config.SuccessContentstringNãoTexto do diálogo de aprovação
Config.FailedHeaderstringNãoTítulo do diálogo de reprovação
Config.FailedContentstringNãoTexto do diálogo de reprovação
Config.PassingScorenumberNãoCampo presente no config de exemplo. O código usa Config.Percent, não este valor
Config.NotifyTypestringNãoCampo presente no config de exemplo. O código sempre usa lib.notify
Config.Interaction.TypestringSim"target", "marker" ou "3dtext" — define como o jogador inicia o exame
Config.Interaction.MarkerLabelstringNãoTexto exibido no marker / texto 3D
Config.Interaction.MarkerTypenumberNãoID do marker do GTA V
Config.Interaction.MarkerColor{r,g,b}NãoCor do marker
Config.Interaction.MarkerSize{x,y,z}NãoEscala do marker
Config.Interaction.MarkerOnFloorboolNãoFixa o marker no chão
Config.Interaction.TargetIconstringNãoÍcone FontAwesome da opção do ox_target
Config.Interaction.TargetLabelstringNãoRótulo da opção do ox_target
Config.Interaction.TargetRadiusvector3NãoTamanho da box zone do ox_target
Config.Interaction.TargetDistancenumberNãoDistância máxima de interação
Config.SpawnCoordsvec4SimOnde o jogador sem cidadania nasce e para onde volta se tentar fugir
Config.ExamCoordsvec3SimPonto da interação que inicia o exame
Config.CompletionCoordsvec4SimPara onde o jogador é teleportado após ser aprovado
Config.citizenZone.coordsvec3SimCentro da zona de confinamento
Config.citizenZone.sizevec3SimDimensões da box zone
Config.citizenZone.rotationnumberSimRotação da box zone
Config.QuestionstabelaSimLista de perguntas — ver seção abaixo
Config.PreExamQuestionstabelaNãoFormulário opcional antes do exame — ver seção abaixo

Fluxo do jogador

  1. No QBCore:Client:OnPlayerLoaded, o client pede a configuração ao servidor e chama mri_Qwhitelist:Server:CheckCitizenship.
  2. Se o jogador não está na whitelist, o servidor o move para o routing bucket 1000 + source (isolado dos demais) e retorna false.
  3. O client teleporta o jogador para Config.SpawnCoords, cria a interação de exame (target, marker ou texto 3D) e a box zone citizenZone.
  4. Um loop a cada Config.TimerTimeout ms verifica se o jogador está dentro da zona. Se sair, ele é teleportado de volta ao spawn com a notificação escapeNotify.
  5. Ao interagir, o jogador responde as perguntas (opcionalmente precedidas do formulário pré-exame). As perguntas e as alternativas são embaralhadas a cada tentativa.
  6. Se o percentual de acertos atingir Config.Percent, o servidor grava o citizenid na tabela mri_qwhitelist, move o jogador para o bucket 0 e o client o teleporta para Config.CompletionCoords, removendo a zona.
  7. Se reprovar, o diálogo de falha é exibido e o jogador pode tentar de novo pela mesma interação.

Perguntas do exame

Cada entrada de Config.Questions é uma pergunta com uma lista de alternativas. A alternativa correta tem value = true.

Config.Questions = {
    {
        question = "O que é Meta Gaming?",
        options = {
            { label = "Uso de informação que o personagem não aprendeu no RP.", value = true },
            { label = "Alternativa errada.", value = false },
            { label = "Eu não sei.", value = false }
        }
    },
}

A ordem das perguntas e das alternativas é embaralhada em runtime. Nada impede marcar mais de uma alternativa como correta, mas o jogador só escolhe uma.


Perguntas pré-exame e webhook do Discord

Quando Config.PreExamQuestions.Enabled = true, um lib.inputDialog é exibido antes do exame. As respostas são enviadas ao servidor, que as encaminha para o webhook configurado como um embed do Discord com o nome, o citizenid e o nome do personagem.

CampoTipoObrigatórioDescrição
EnabledboolSimLiga/desliga o formulário
FormatPhoneboolNãoConverte campos com kind = "phone" em link https://wa.me/55<numero> no embed
WebHookstringSim (se Enabled)URL do webhook do Discord. Sem ela, o envio é abortado com aviso no console
labelstringNãoTítulo do diálogo
informationtabelaSimCampos do lib.inputDialog. Cada campo aceita type, label, placeholder, required, min, max e kind

O kind é usado para formatar o valor no embed (por exemplo kind = "phone") e não é uma validação do ox_lib.


O recurso não registra comandos de chat. O acesso ao gerenciamento é feito de duas formas:

  • Com mri_Qbox iniciado — o menu “Gerenciar Whitelist” é adicionado automaticamente ao menu gerencial do mri_Qbox via exports['mri_Qbox']:AddManageMenu.
  • Sem mri_Qbox — o client registra o callback mri_Qwhitelist:Client:ManageWhitelistMenu, que outro recurso pode disparar para abrir o menu.

O menu permite:

  • Ativar/desativar a whitelist.
  • Definir o local de spawn e o local do exame usando a mira (exports.mri_Qbox:GetRayCoords). A opção “Zona de Exame” existe mas está desabilitada no código.
  • Ajustar o percentual de acertos.
  • Criar, editar e remover perguntas e alternativas, e marcar qual alternativa é correta.
  • Liberar ou revogar a whitelist de um jogador, informando o citizenid ou o source.

Toda saída do menu (onExit) grava a configuração atual na tabela mri_qwhitelistcfg.


Banco de dados

Ambas as tabelas são criadas automaticamente no onResourceStart se não existirem.

mri_qwhitelist

ColunaTipoDescrição
idINT AUTO_INCREMENTChave primária
citizenVARCHAR(50) UNIQUEcitizenid do personagem liberado

mri_qwhitelistcfg

ColunaTipoDescrição
idINT AUTO_INCREMENTChave primária (o recurso sempre usa id = 1)
configLONGTEXTTabela Config serializada em JSON

Para forçar o recurso a voltar a usar o server/config.lua, apague a linha da tabela mri_qwhitelistcfg.


Integrações

mri_Qbox

Se o mri_Qbox estiver iniciado, o menu de gerenciamento é registrado no menu gerencial central e as opções de captura de coordenadas passam a usar exports.mri_Qbox:GetRayCoords().


Entrypoints para outros recursos

Todos os entrypoints são callbacks do ox_lib.

Callbacks do servidor

-- Retorna a tabela Config atual
local config = lib.callback.await('mri_Qwhitelist:Server:GetConfig', false)
 
-- Salva a configuração (persiste em mri_qwhitelistcfg)
lib.callback.await('mri_Qwhitelist:Server:SaveConfig', false, config)
 
-- true se o jogador tem cidadania (ou se a whitelist está desativada).
-- Efeito colateral: move o jogador para o bucket isolado quando não tem.
local ok = lib.callback.await('mri_Qwhitelist:Server:CheckCitizenship', false)
 
-- Libera um jogador. `identifier` aceita citizenid (string) ou source (number).
-- Omitido, usa o próprio source.
lib.callback.await('mri_Qwhitelist:Server:AddCitizenship', false, identifier)
 
-- Revoga a whitelist e devolve o jogador ao bucket isolado.
lib.callback.await('mri_Qwhitelist:Server:RemoveCitizenship', false, identifier)
 
-- Envia um formulário pré-exame ao webhook configurado.
lib.callback.await('mri_Qwhitelist:Server:SendPreExamData', false, data)

Callbacks do client

-- Marca o exame como concluído, remove a zona e teleporta para CompletionCoords
lib.callback.await('mri_Qwhitelist:Client:AddCitizenship', playerSource, false)
 
-- Recria a zona de confinamento e devolve o jogador ao spawn
lib.callback.await('mri_Qwhitelist:Client:RemoveCitizenship', playerSource, false)
 
-- Abre o menu de gerenciamento (registrado apenas quando mri_Qbox não está iniciado)
lib.callback.await('mri_Qwhitelist:Client:ManageWhitelistMenu', playerSource, false)

Estrutura de arquivos

mri_Qwhitelist/
├── client/
│   ├── main.lua              — fluxo do exame, teleportes, zona de confinamento
│   └── creator.lua           — menus ox_lib de gerenciamento (perguntas, locais, liberar/revogar)
├── server/
│   ├── main.lua              — callbacks, MySQL, routing buckets, webhook do Discord
│   └── config.sample.lua     — configuração modelo (copiar para config.lua)
├── interactions/
│   ├── target.lua            — interação via ox_target
│   ├── marker.lua            — interação via marker 3D
│   ├── text.lua              — interação via texto 3D
│   └── zone.lua              — wrapper da box zone do ox_lib
└── fxmanifest.lua