Ir al contenido

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…

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 true
end
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))
end

Todos 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.

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.

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, o nil. Úsalo para lo que necesite al jugador dentro del juego (notificaciones, eventos). Comprueba siempre que no sea nil.
Handlers.OnSaleCompleted = function(data)
if data.sellerSource then
TriggerClientEvent('chat:addMessage', data.sellerSource, { args = { 'Market', 'Has vendido ' .. data.itemLabel } })
end
end

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
  1. 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 validate te dice si falta alguno.
  2. 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 de Config.HandlerRuntime.SlowWarningMs (100 ms), la consola te avisa. Para trabajo lento en hooks de ciclo de vida, abre un hilo: CreateThread(function() ... end).
  3. 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.
  4. Los hooks de ciclo de vida no pueden cancelar nada. Si necesitas impedir una acción, usa el guard correspondiente.

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
}
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