Ir al contenido

Probar los hooks

Esta página explica cómo comprobar que tus hooks funcionan antes de ponerlos en producción: cómo ver dispararse cada uno, los comandos de prueba incluidos, cómo acortar los tiempos y una lista con todos los hooks.

  1. Activa los comandos de prueba en config.lua:

    Config.Debug = true

    Esto activa /markettest y /marketdebug. Los dos funcionan solo para administradores.

  2. Hazte administrador en server.cfg (o añade tu identificador a Config.Admin.Identifiers):

    server.cfg
    add_ace group.admin marketplace.admin allow
    add_principal identifier.license:TU_LICENCIA group.admin
  3. Opcional: registra cada hook con su duración en la consola:

    Config.HandlerRuntime = {
    Debug = true,
    -- deja el resto como está
    }
  4. Reinicia el recurso después de cada cambio en config.lua o handlers.lua:

    Ventana de terminal
    ensure sl-marketplace

    La consola debería mostrar handlers API ready. Si dice the handlers API is incomplete, se ha renombrado o borrado algún hook (ver /markethandlers validate).

Muchos hooks necesitan dos jugadores (un comprador y un vendedor, un atracador y un comprador). Puedes abrir un segundo cliente de FiveM en el mismo PC con la opción de arranque -cl2:

  1. Crea un acceso directo a FiveM.exe y añade -cl2 al final de Destino.
  2. Abre FiveM normalmente y después abre el acceso directo: arranca un segundo cliente con su propia identidad.
  3. Conecta los dos a tu servidor de pruebas (localhost:30120).

Pega este bloque al final del todo de server/handlers.lua. Envuelve cada hook para que muestre su nombre y su data completo en la consola del servidor, y después ejecuta tu código como siempre (los guards siguen bloqueando exactamente igual).

server/handlers.lua
-- ─── SOLO PARA PRUEBAS: muestra cada hook. Bórralo antes de ir a producción. ───
local function printable(value, depth)
depth = depth or 0
local kind = type(value)
if kind == 'vector2' or kind == 'vector3' or kind == 'vector4' then return tostring(value) end
if kind ~= 'table' then return value end
if depth > 5 then return '...' end
local out = {}
for k, v in pairs(value) do out[k] = printable(v, depth + 1) end
return out
end
for name, original in pairs(Handlers) do
Handlers[name] = function(data)
print(('^3[hook] %s^7\n%s'):format(name, json.encode(printable(data), { indent = true })))
return original(data)
end
end

Reinicia el recurso y haz cualquier cosa en el marketplace: cada hook aparece con sus datos, por ejemplo:

[hook] OnSaleCompleted
{
"buyerName": "Ruben",
"item": "bread",
"basePrice": 50,
"grossPaid": 50,
"deliveryMode": "mailbox",
"stage": "settled",
...
}

Pega esto al final de client/handlers.lua. Los prints aparecen en la consola F8 del jugador que recibe cada evento.

client/handlers.lua
-- ─── SOLO PARA PRUEBAS: muestra cada evento de cliente. Bórralo antes de ir a producción. ───
local EVENTS = {
'onPurchaseCompleted', 'onDeliveryArrived', 'onDeliveryResult', 'onAuctionWon', 'onSaleMade',
'onInPersonTradeCompleted', 'onListingCreated', 'onListingCancelled', 'onListingBumped',
'onBidPlaced', 'onOutbid', 'onMailboxClaimed', 'onMessageReceived', 'onRefundStatusChanged',
'onInPersonTradeStarted', 'onInPersonMeetUpdated', 'onInPersonTradeCancelled', 'onDeliveryRobbed',
'onSkillCheckStarted', 'onSkillCheckPhase', 'onSkillCheckFinished',
}
local function printable(value, depth)
depth = depth or 0
local kind = type(value)
if kind == 'vector2' or kind == 'vector3' or kind == 'vector4' then return tostring(value) end
if kind ~= 'table' then return value end
if depth > 5 then return '...' end
local out = {}
for k, v in pairs(value) do out[k] = printable(v, depth + 1) end
return out
end
for _, name in ipairs(EVENTS) do
AddEventHandler('sl-marketplace:' .. name, function(data)
print(('[client event] %s %s'):format(name, json.encode(printable(data))))
end)
end

Diagnóstico de los hooks incluido en el script. Funciona desde la consola del servidor o en el juego para administradores. Se activa con Config.HandlerRuntime.EnableDiagnosticsCommand (activado por defecto); el nombre es Config.HandlerRuntime.DiagnosticsCommand.

Comando Qué muestra
/markethandlers Resumen: llamadas totales, correctas, errores, bloqueos de guards y llamadas lentas
/markethandlers all Una línea por hook: llamadas, errores, bloqueos, llamadas lentas, tiempo medio y máximo
/markethandlers errors Solo los hooks con errores, llamadas lentas o que faltan
/markethandlers validate Comprueba que los 38 hooks existen y son funciones. Lista los que faltan, los no válidos y los que sobran
/markethandlers reset Pone los contadores a cero

Ejemplo de salida de all:

[sl-marketplace][handler diagnostics] OnSaleCompleted lifecycle calls=3 ok=3 errors=0 denials=0 slow=0 avg=0.33ms max=1ms
[sl-marketplace][handler diagnostics] CanBuyListing guard calls=4 ok=4 errors=0 denials=1 slow=0 avg=0.25ms max=1ms

Una buena rutina: reset, haz la acción que quieres probar, all. El hook que esperas debería tener calls=1; si sube errors, la consola de arriba muestra el error y su traceback.

Comandos para probar los hooks tú solo, sin un segundo jugador. Necesitan Config.Debug = true y permisos de administrador; se ejecutan en el juego (no desde la consola).

Comando Qué hace
/markettest help Muestra cómo se usa
/markettest listing [modo] [tipo] [objeto] [precio] [cantidad] Crea un anuncio de un vendedor de prueba (un jugador ficticio) para que puedas comprarlo o pujar tú mismo
/markettest grant [dinero] [objeto] [cantidad] Te da dinero y/o objetos
/markettest clean Borra el vendedor de prueba y todos sus anuncios

Para listing:

Parámetro Valores Por defecto
modo mailbox, meetup, npc mailbox
tipo fixed, auction, blind, dutch, bundle fixed
objeto Cualquier nombre de objeto bread
precio Precio (nunca por debajo de Config.MinPrice) 10
cantidad Cantidad 1

Las subastas de prueba (auction, blind) terminan a los 5 minutos. Ejemplos:

/markettest grant 5000
/markettest listing mailbox fixed bread 50 -> cómpralo: CanBuyListing, OnSaleCompleted
/markettest listing npc fixed water 100 -> cómpralo: furgoneta de reparto, OnDeliveryCompleted
/markettest listing mailbox auction bread 100 -> puja y espera 5 min: OnAuctionEnded
/markettest listing mailbox dutch bread 1000 -> cómpralo: OnSaleCompleted con listingType dutch
/markettest clean

Algunos hooks solo se disparan después de un tiempo. En el servidor de pruebas, baja temporalmente estos valores:

Para probar Cambia Valor de prueba
Caducidad de tus propias subastas Config.Auction.MinDurationMinutes 1
Bajadas de precio holandesas Config.Auctions.Dutch.MinIntervalMinutes 1
Caducidad de inversas y trueques Créalos con la duración más corta —
timeout del repartidor Config.Gameplay.NPCDelivery.DeliveryTimeout 30
Tiempo límite del intercambio en persona Config.InPersonTrade.TimeoutMinutes 2
Caducidad de la propuesta de encuentro Config.InPersonTrade.MeetRequestTimeoutSeconds 30
Abandonar la zona de encuentro Config.InPersonTrade.AbandonTimeoutSeconds 15
Subir anuncios varias veces Config.RateLimits.bump { max = 10, window = 60 }

Hay dos tiempos que no se pueden acortar desde config.lua: los anuncios caducados y las subastas terminadas se revisan cada 30 segundos, y la aprobación automática de reembolsos (AutoApproveAfterHours) se revisa cada hora.

Haz que bloquee todo, comprueba el mensaje y vuelve a poner tu lógica:

Handlers.CanPlaceBid = function(data)
print(('[test] %s de %s: $%s (mínimo $%s)'):format(data.action, data.bidderName, data.amount, data.minimumBid))
return false, 'Prueba: las pujas están bloqueadas'
end

Lo que deberías ver: el mensaje Prueba: las pujas están bloqueadas en el juego, tu línea en la consola, que no cambia nada (no se reserva dinero), y que /markethandlers all muestra denials=1 en CanPlaceBid.

Prueba también el caso de fallo: haz que el guard lance un error (error('boom')). La acción tiene que quedar bloqueada y la consola tiene que mostrar el error con su traceback. Eso confirma que una integración rota nunca dejará pasar acciones (salvo que pongas GuardFailurePolicy = 'allow').

Cada hook, cómo lanzarlo y si puedes hacerlo tú solo.

Hook Cómo lanzarlo ¿Solo?
CanCreateListing Publica un anuncio desde el marketplace Sí
CanBuyListing Compra un anuncio (/markettest listing) Sí
CanPlaceBid Puja, o fija un máximo automático, en una subasta de prueba Sí
CanCancelListing Cancela uno de tus anuncios Sí
CanCreateBarterProposal Envía una propuesta a un anuncio de trueque / contraoferta 2 jugadores
CanAcceptBarterProposal Acepta una propuesta de trueque o una contraoferta 2 jugadores
CanConfirmInPersonTrade Confirma un intercambio en persona 2 jugadores
CanRequestRefund Pide un reembolso de una compra 2 jugadores
Hook Cómo lanzarlo ¿Solo?
OnSaleCompleted Compra un anuncio de prueba Sí
OnAuctionEnded Puja en una subasta de prueba y espera 5 min Sí
OnReverseOfferAccepted Acepta una oferta en tu subasta inversa 2 jugadores
OnListingCreated Publica desde el marketplace Sí
OnListingExpired Subasta de prueba sin pujas, espera 5 min Sí
OnListingCancelled Cancela un anuncio (y desde /marketadmin) Sí
OnListingBumped Sube un anuncio Sí
OnBidPlaced Puja en una subasta de prueba Sí
OnProxyBidSet Fija un máximo automático Sí
OnDeliveryCompleted Compra un anuncio de prueba npc y espera a la furgoneta Sí
OnDeliveryFailed Destruye la furgoneta, o desconéctate durante el reparto Sí
OnDeliveryRobbed Atraca la furgoneta de reparto de otro jugador 2 jugadores
OnInPersonTradeCompleted Completa un intercambio en persona 2 jugadores
OnInPersonTradeCancelled Cancela un intercambio en persona 2 jugadores
OnMeetupStatusChanged Propón / acepta un punto de encuentro en el chat 2 jugadores
OnBarterProposalCreated Envía una propuesta de trueque 2 jugadores
OnBarterProposalStatusChanged Acepta, contraoferta o rechaza una propuesta 2 jugadores
OnRefundRequested Pide un reembolso 2 jugadores
OnRefundApproved Aprueba un reembolso (o fuérzalo en /marketadmin) 2 jugadores
OnRefundRejected Rechaza un reembolso 2 jugadores
OnPlayerRated Valora a un vendedor después de comprarle 2 jugadores
OnMessageSent Envía un mensaje en un anuncio 2 jugadores
OnFavoriteChanged Marca un anuncio como favorito Sí
OnFollowChanged Sigue a un vendedor 2 jugadores
OnWishlistChanged Añade un objeto a tu lista de deseos Sí
OnReportCreated Reporta un anuncio Sí
OnProfileCreated Crea tu tienda por primera vez Sí
OnProfileUpdated Edita tu perfil Sí
OnMailboxClaimed Recoge un objeto o dinero del buzón Sí
OnAdminAction Cancela, bloquea, desbloquea, resuelve un reporte o fuerza un reembolso en /marketadmin Sí
Evento Lo recibe ¿Solo?
onPurchaseCompleted Comprador Sí
onSaleMade Vendedor 2 jugadores
onAuctionWon Ganador Sí
onListingCreated, onListingCancelled, onListingBumped Vendedor Sí
onBidPlaced Quien puja Sí
onOutbid El que iba ganando 2 jugadores
onMailboxClaimed Quien recoge Sí
onMessageReceived Destinatario 2 jugadores
onRefundStatusChanged Comprador y vendedor 2 jugadores
onDeliveryArrived, onDeliveryResult Comprador Sí
onDeliveryRobbed Atracador 2 jugadores
onInPersonTradeStarted, onInPersonMeetUpdated, onInPersonTradeCompleted, onInPersonTradeCancelled Los dos 2 jugadores
onSkillCheckStarted, onSkillCheckPhase, onSkillCheckFinished Atracador 2 jugadores

Mi hook no se dispara nunca. Comprueba que el nombre es exactamente el del archivo original (mayúsculas incluidas) y que has reiniciado el recurso. /markethandlers validate lista los hooks que faltan o se han renombrado. En los eventos de cliente, comprueba que el texto del evento empieza por sl-marketplace:.

Después de editar un guard se bloquea todo. Tu guard lanza un error, y los guards fallan cerrados. La consola del servidor muestra [sl-marketplace][handlers][ERROR] CanXxx: con el traceback.

La consola dice Slow execution: 250 ms. Un hook tardó más que SlowWarningMs. Mueve el trabajo lento (peticiones HTTP, consultas grandes) dentro de un CreateThread(function() ... end) en el hook, y nunca lo hagas en un guard.

No me llega un evento de cliente. Casi todos van a un solo jugador (ver Eventos de cliente). Los eventos del vendedor no se disparan para el vendedor de /markettest, porque nunca está conectado.

/markettest dice Config.Debug is disabled. Pon Config.Debug = true y reinicia el recurso. Acuérdate de volver a ponerlo en false en producción.