Cómo funcionan las integraciones#
El panel nunca habla directamente con tu framework, inventario o script de clima. Cada uno se alcanza a través de un pequeño archivo de bridge abierto, y el bridge correcto se elige al iniciar mirando qué recursos están en ejecución.
Capa | Compatibles | Sin ella |
|---|---|---|
Framework |
| Standalone: la moderación, los baneos, los reportes, los logs y las herramientas del servidor funcionan; el dinero, los trabajos, los ítems, los garajes y el acceso por grupos no. Concede acceso con |
Inventario |
| Give Item no tiene lista de ítems y Open Inventory queda desactivado. |
Llaves de vehículos |
| Los vehículos generados y dados están desbloqueados, pero se entregan sin llave. |
Clima |
| El menú controla el clima y la hora por sí mismo y mantiene sincronizados a todos los jugadores. |
Médico |
| Revivir y curar usan las natives del propio juego; el hambre y la sed pasan por el framework. |
Combustible |
| Los coches generados se llenan con el nivel de combustible del propio juego. |
Voz | Cualquier voz basada en Mumble, como | La escucha de voz de Live View no está disponible. |
Capturas de pantalla |
| Los logs de Discord se envían sin captura de pantalla. |
Base de datos |
| Obligatorio. El único, con |
Cada integración opcional tiene un respaldo silencioso. Si falta un recurso, su función queda desactivada en lugar de dar error. La línea de consola
[qs-adminmenu] Framework loaded:te indica qué bridge de framework se eligió.
Forzar la detección#
La detección solo ve los recursos que ya están iniciados, así que todo lo que quieras que encuentre debe estar por encima de ensure qs-adminmenu. Cuando hay dos candidatos para la misma capa en ejecución a la vez (dos inventarios, por ejemplo) o un recurso se ha renombrado en tu servidor, fija el valor a mano en config/main.lua:
-- Auto, the default
Config.Inventory = DependencyCheck(inventories) or 'standalone'
-- Manual: skip detection entirely
Config.Inventory = 'ox' -- qs · ox · qb · tgiann · codem · standalone
Config.Framework = 'qb' -- esx · qb (QBCore and Qbox) · none
Config.KeySystem = 'qs' -- qs · qb · cd_garage
Config.MedicalSystem = 'qs' -- qs · tk · standaloneClima#
La capa de clima es la única excepción: se vuelve a comprobar cada vez que un recurso de clima se inicia o se detiene, así que su lugar en server.cfg no importa. Config.WeatherResources muestra los candidatos en orden de prioridad: qs-zoneweather primero a propósito, porque también responde al nombre qb-weathersync. Para forzar uno, pon Config.ForceWeatherSystem en 'zoneweather', 'qb', 'codem' o 'standalone'.
Garajes#
Los vehículos dados y el Player Garage leen y escriben en la propia tabla de vehículos con dueño de tu framework. Un coche dado desde el panel se guarda en el garaje indicado en Config.DefaultGarage: central en ESX, pillboxgarage en QBCore. Cámbialo por un garaje que exista en tu servidor.
Tu sistema de servicio#
Con Config.RequireDuty.enabled activado, el menú pregunta a una función si un miembro del staff está de servicio antes de abrir el panel, el noclip o Fast Actions. Conéctala a tu propio script en custom/server.lua:
-- custom/server.lua
---@param src number
---@param feature 'menu' | 'noclip' | 'fastAction'
function IsAdminOnDuty(src, feature)
return exports['your-duty-script']:IsOnDuty(src) == true
endSi la función da error (porque su recurso se detuvo, por ejemplo), esa opción se bloquea, nunca se abre en silencio. Desactivar el noclip siempre está permitido.
Logs de Discord#
Cada evento registrado también se puede publicar en Discord como embed, con el jugador, sus identificadores y los detalles. Las URLs están en server/webhooks.lua, agrupadas por categoría, con una URL por evento o un _default para toda la categoría:
-- server/webhooks.lua
_G['webhook'] = {
player_actions = {
_default = 'https://discord.com/api/webhooks/...', -- every player action
ban = 'https://discord.com/api/webhooks/...', -- bans get their own channel
},
admin_self = { _default = '' },
resources = { _default = '' },
console = { _default = '' },
reports = { created = '', updated = '', closed = '' },
_global = '', -- anything with no URL of its own
}Un log va a la URL más específica que encuentre: la de su propio evento, después el _default de su categoría y después _global. Deja las tres vacías y ese evento se guarda en la base de datos pero no se publica.
Categoría | Qué va ahí |
|---|---|
| Warn, kick, ban, unban, conexiones bloqueadas, freeze, goto, bring, spectate, Live View, ítems, dinero, trabajos, revive, heal. |
| Poderes del staff, revive all, kick all, vehículos generados, anuncios, limpieza del mundo, teletransportes. |
| Coches dados, generaciones y eliminaciones en garajes, cambios en el catálogo. |
| Acciones sobre recursos, comandos de consola, guardados de archivos y recargas. |
| Ajustes de la cola, whitelist y niveles; clima y hora; densidad y supresión. |
| Entradas, salidas, chat, muertes; explosiones, daño de armas sospechoso, errores del servidor. |
| Reportes creados, actualizados y cerrados. Añade el grupo |
Config.Logs.Events en config/main.lua activa o desactiva la publicación en Discord de cada evento (webhook), su lugar en la página Logs (ui) y el color de su embed. Config.Logs.Webhook define el nombre y el avatar del bot y si se muestra la IP del jugador (showIp, desactivado por defecto). El daño de armas por encima de WeaponDamageSuspiciousThreshold (200) se marca como sospechoso.
Capturas de pantalla en los logs#
Con screenshot-basic en ejecución y un token de Fivemanage en _G['fivemanage'].webhook al final de server/webhooks.lua, los logs sobre un jugador llevan una captura de su pantalla.
Screen Watch, Live View y TURN#
Screen Watch y Live View transmiten vídeo directamente desde el juego del jugador al del admin (WebRTC). El servidor solo les pasa a los dos la dirección del otro. El servidor STUN público de Google viene configurado de serie, y basta en la mayoría de conexiones domésticas. Los jugadores detrás de un NAT estricto, CGNAT o algunas conexiones móviles necesitan un relay TURN, o su transmisión se queda en Connecting.
La vía recomendada: las credenciales se obtienen al iniciar y se renuevan antes de que caduquen.
Abre Cloudflare → Realtime, ve a la pestaña TURN y pulsa Create TURN App (el producto TURN, no SFU).
Copia el TURN Token ID y el API Token (solo se muestra una vez).
Rellénalos y actívalo:
-- config/main.lua
Config.ScreenWebRTC.cloudflare = {
enabled = true,
turnKeyId = 'your TURN Token ID',
apiToken = 'your API Token',
ttlSeconds = 86400,
refreshSkewSeconds = 600,
retryOnFailMs = 30000,
}Ejecuta adminmenu:turn:status en la consola del servidor para ver los servidores en caché y cuándo caducan.
Cualquier proveedor de TURN (Metered, coturn en tu propia máquina…). Mantén los endpoints UDP, TCP y TLS para tener más posibilidades de conectar:
-- config/main.lua
Config.ScreenWebRTC.iceServers = {
{ urls = 'stun:stun.l.google.com:19302' },
{
urls = {
'turn:turn.example.com:3478?transport=udp',
'turn:turn.example.com:3478?transport=tcp',
'turns:turn.example.com:5349?transport=tcp',
},
username = 'user',
credential = 'pass',
},
}La calidad de imagen se configura en dos sitios: Config.ScreenWebRTC.video para la ventana individual de Screen Watch (1600×900, 30 fps por defecto) y Config.LiveView.gridVideo para las casillas más pequeñas de Live View (640×360, 15 fps). Live View permite que un admin vea hasta 9 jugadores a la vez, y que un jugador sea visto por hasta 3 admins.
Escucha de voz#
Listen to voice sube la voz del jugador al volumen máximo en el cliente del admin y silencia al admin para ese jugador, mediante las natives de Mumble de FiveM, que es lo que usan pma-voice y la mayoría de recursos de voz. Desactívalo con Config.LiveView.Voice.enabled = false. Para un recurso de voz con su propia API, edita los dos archivos abiertos server/custom/liveview_voice.lua y client/custom/liveview_voice.lua.
Añadir un backend#
Dar soporte a un recurso que no está en la lista requiere un archivo y una línea. Cada bridge es pequeño, sin cifrar y protegido para que solo se ejecute cuando es el elegido.
Copia el bridge más parecido
Los frameworks están en
custom/<name>/; los inventarios, el clima, el médico y los garajes enserver/custom/<kind>/yclient/custom/<kind>/. Copia el más parecido al tuyo (server/custom/inventory/ox.lua, por ejemplo) y renómbralo.Cambia las llamadas de dentro
Mantén la protección en la primera línea y cambia los exports por los de tu recurso:
lua if Config.Inventory ~= 'myinv' then return end function sv_inventory:formatItemList() ... end function sv_inventory.openInventory(adminSource, targetId) ... endRegístralo para la detección
Añade tu recurso a la tabla correspondiente en
config/main.lua(['my-inventory'] = 'myinv'eninventories) o ponConfig.Inventory = 'myinv'directamente.
Si es más de lo que quieres asumir, abre un ticket con el nombre del recurso.
Idiomas#
Se incluyen 41 traducciones en locales/: ar bg bn ca cs da de el en es et fa fi fr he hi hr hu id it ja ko lt lv ms nl no pl pt ro ru sk sl sv sw th tr uk ur vi zh. Pon Config.Locale en uno de esos códigos; cubre el panel, el Report Center, los avisos que ven los jugadores y la pantalla de baneo. Config.Intl.locales define cómo se formatean los números, las fechas y las duraciones: 'es-ES', 'de-DE', etc.
Para añadir otro idioma, copia en.json con el nuevo código, tradúcelo y pon el locale en ese código. Todo lo que quede sin traducir pasa a inglés.



