Cómo funcionan los hooks
SL-Marketplace incluye dos archivos abiertos que puedes editar libremente, aunque el resto del script esté protegido por el escrow:
| Archivo | Se ejecuta en | Qué contiene |
|---|---|---|
server/handlers.lua |
Servidor | 38 hooks: 8 guards que pueden bloquear una acción y 30 hooks de ciclo de vida que te avisan de que algo ha pasado |
client/handlers.lua |
Cliente | 21 eventos que se disparan en el juego del propio jugador, para integraciones visuales |
Además, config.lua tiene dos callbacks: Config.Notify y Config.OnDeliveryRobbed.
Úsalos para conectar el marketplace con el resto de tu servidor: avisos a la policía, trabajos, XP, logros, logs, impuestos, notificaciones propias, sonidos…
Los tres tipos de hooks
Sección titulada «Los tres tipos de hooks»Guards — Handlers.Can* (servidor)
Sección titulada «Guards — Handlers.Can* (servidor)»Un guard se ejecuta antes de que ocurra una acción y puede impedirla. El marketplace ya ha validado la petición (el jugador tiene el dinero, el objeto existe, el precio está dentro del rango…) pero todavía no ha tocado nada.
Handlers.CanCreateListing = function(data) if data.price > 50000 then return false, 'No puedes publicar nada por encima de $50.000' end return trueend| Lo que devuelves | Resultado |
|---|---|
true o nada |
La acción sigue adelante |
false, 'motivo' |
La acción se bloquea y el jugador ve 'motivo' |
false (sin motivo) |
Se bloquea con el mensaje por defecto (actionBlockedByServer en locales/, en el idioma del jugador) |
{ allowed = false, reason = '…' } |
Igual que false, 'motivo' |
| La función lanza un error | Se bloquea (ver fallan cerrados) |
Todos los guards, uno a uno, en Guards del servidor.
Hooks de ciclo de vida — Handlers.On* (servidor)
Sección titulada «Hooks de ciclo de vida — Handlers.On* (servidor)»Un hook de ciclo de vida se ejecuta después de que algo haya pasado: una venta, una puja, una subasta terminada, un atraco… No puede deshacerlo y lo que devuelva se ignora. Aquí es donde das XP, mandas logs, cobras impuestos o avisas a la policía.
Handlers.OnSaleCompleted = function(data) TriggerEvent('yourxp:addXP', data.sellerId, math.floor(data.basePrice / 100))endTodos los hooks en Hooks del servidor.
Eventos de cliente — sl-marketplace:on* (cliente)
Sección titulada «Eventos de cliente — sl-marketplace:on* (cliente)»Se disparan en el juego de un solo jugador (el comprador, el vendedor, el atracador…), nunca para todo el servidor. Úsalos para sonidos, efectos de pantalla, notificaciones propias o interfaz.
AddEventHandler('sl-marketplace:onSaleMade', function(data) PlaySoundFrontend(-1, 'WAYPOINT_SET', 'HUD_FRONTEND_DEFAULT_SOUNDSET', true)end)Todos los eventos en Eventos de cliente.
La tabla data
Sección titulada «La tabla data»Cada hook del servidor recibe una sola tabla, data. Sus campos dependen del hook y están listados para cada uno en esta referencia. Cuatro campos están siempre:
| Campo | Tipo | Significado |
|---|---|---|
data.hook |
string | Nombre del hook, por ejemplo 'OnSaleCompleted' |
data.timestamp |
number | Hora Unix (segundos) del evento |
data.schemaVersion |
number | Versión del formato de datos, ahora mismo 1 |
data.resource |
string | Nombre de la carpeta del recurso, por ejemplo 'sl-marketplace' |
data es una copia: cambiarla dentro de tu hook no cambia lo que hace el marketplace.
Identificadores y sources
Sección titulada «Identificadores y sources»La mayoría de hooks te dan un identificador y un source de cada jugador:
…Id(sellerId,buyerId,winnerId…) es el identificador de ESX (license:…,char1:…). Está siempre, aunque el jugador esté desconectado. Úsalo para todo lo que se guarde.…Source(sellerSource,buyerSource…) es el ID de servidor del jugador si está conectado ahora mismo, onil. Úsalo para lo que necesite al jugador dentro del juego (notificaciones, eventos). Comprueba siempre que no seanil.
Handlers.OnSaleCompleted = function(data) if data.sellerSource then TriggerClientEvent('chat:addMessage', data.sellerSource, { args = { 'Market', 'Has vendido ' .. data.itemLabel } }) endendCampos de dinero
Sección titulada «Campos de dinero»Las ventas traen varias cantidades. Usa las explícitas; price y finalPrice solo se mantienen por compatibilidad con integraciones antiguas.
| Campo | Significado |
|---|---|
basePrice |
Precio del anuncio realmente pagado (con el descuento flash aplicado), sin extras |
grossPaid |
Lo que pagó el comprador en total: basePrice + entrega, recargo del modo y seguro |
platformFee |
Comisión que se retira de la economía (Config.ServerFee, 5% por defecto) |
deliveryFee |
modeSurcharge + npcDeliveryFee |
modeSurcharge |
Extra por un modo de entrega que el vendedor no marcó como preferido (Config.DeliveryBuyerFee) |
npcDeliveryFee |
Coste del repartidor NPC (Config.Gameplay.NPCDelivery.Cost) |
insuranceFee |
Seguro opcional del reparto |
sellerPayout |
Lo que recibe el vendedor |
- No renombres nada. El script llama a
Handlers.CanCreateListing,Handlers.OnSaleCompleted… por su nombre, y a los eventos de cliente por su texto exacto. Una función o un evento renombrado simplemente no se dispara nunca. Tampoco los borres: un guard que falta cuenta como un error, así que la acción que protege queda bloqueada siempre./markethandlers validatete dice si falta alguno. - Haz que los hooks sean rápidos. Se ejecutan dentro de la operación del marketplace. No uses
Wait()ni consultas largas a la base de datos en los guards. Si un hook tarda más deConfig.HandlerRuntime.SlowWarningMs(100 ms), la consola te avisa. Para trabajo lento en hooks de ciclo de vida, abre un hilo:CreateThread(function() ... end). - Los errores quedan contenidos. Si tu código lanza un error, la consola lo muestra con su traceback y la operación del marketplace sigue adelante (en los guards: se bloquea, ver abajo). Un fallo en tu hook nunca deja una venta a medias.
- Los hooks de ciclo de vida no pueden cancelar nada. Si necesitas impedir una acción, usa el guard correspondiente.
Los guards fallan cerrados
Sección titulada «Los guards fallan cerrados»Si un guard lanza un error, la acción se bloquea. Es a propósito: si tu integración de trabajos o permisos se rompe, no puede convertirse en una forma de saltarse tus reglas.
Puedes cambiarlo en config.lua, pero solo para guards que no sean de seguridad:
Config.HandlerRuntime = { GuardFailurePolicy = 'deny', -- 'allow' deja pasar la acción cuando un guard falla}Config.HandlerRuntime
Sección titulada «Config.HandlerRuntime»| Opción | Por defecto | Qué hace |
|---|---|---|
Debug |
false |
Muestra cada ejecución de hook y cuánto tardó |
SlowWarningMs |
100 |
Avisa en la consola cuando un hook tarda más de esto (ms). 0 lo desactiva |
TrackMetrics |
true |
Cuenta llamadas, errores, bloqueos y tiempos para /markethandlers |
ValidateOnStart |
true |
Comprueba al arrancar que todos los hooks existen y son funciones |
EnableDiagnosticsCommand |
true |
Registra el comando de diagnóstico |
DiagnosticsCommand |
'markethandlers' |
Nombre del comando de diagnóstico |
GuardFailurePolicy |
'deny' |
Qué pasa cuando un guard lanza un error: 'deny' o 'allow' |
DefaultDenyMessage |
(sin definir) | Mensaje fijo cuando un guard bloquea sin motivo. Sin definir = el actionBlockedByServer traducido |
Siguientes pasos
Sección titulada «Siguientes pasos»- Guards del servidor: los 8 guards, campo a campo.
- Hooks del servidor: los 30 hooks de ciclo de vida y los dos callbacks de
config.lua. - Eventos de cliente: los 21 eventos de cliente.
- Probar los hooks: cómo ver dispararse cada hook, paso a paso.