Guards del servidor
Los guards están en server/handlers.lua y se llaman Handlers.Can…. Cada uno se ejecuta después de que el marketplace haya comprobado la petición y antes de que cambie nada (dinero, objetos, base de datos). Devuelve false, 'motivo' para bloquear la acción; el jugador ve el motivo. Las reglas de lo que puedes devolver están en Cómo funcionan los hooks.
Todos los guards reciben estos campos, más los que se indican en cada uno:
| Campo | Tipo | Significado |
|---|---|---|
actorSource |
number | ID de servidor del jugador que hace la acción |
actorId |
string | Su identificador |
actorName |
string | Su nombre de jugador |
action |
string | Lo que intenta hacer (se indica en cada guard) |
Resumen
Sección titulada «Resumen»| Guard | Protege | Valores de action |
|---|---|---|
CanCreateListing |
Publicar un anuncio de cualquier tipo | create_listing |
CanBuyListing |
Comprar, Buy Now y aceptar la oferta de una subasta inversa | buy_listing, accept_reverse_offer |
CanPlaceBid |
Pujas manuales y pujas automáticas (proxy) | place_bid, set_proxy_bid |
CanCancelListing |
Que el vendedor cancele su propio anuncio | cancel_listing |
CanCreateBarterProposal |
Propuestas de trueque y contraofertas del vendedor | create_barter_proposal, create_barter_counter |
CanAcceptBarterProposal |
Aceptar una propuesta de trueque o una contraoferta | accept_barter_proposal |
CanConfirmInPersonTrade |
La confirmación de cada jugador en un intercambio en persona | confirm_in_person_trade |
CanRequestRefund |
Que un comprador abra una solicitud de reembolso | request_refund |
CanCreateListing
Sección titulada «CanCreateListing»Cuándo: un jugador publica un anuncio (precio fijo, subasta, a ciegas, holandesa, inversa, lote o trueque), después de validar el objeto, la cantidad, el precio, la duración y los modos de entrega, y antes de quitarle el objeto del inventario.
Si lo bloqueas: no se le quita nada al jugador y el anuncio no se crea.
| Campo | Significado |
|---|---|
sellerSource, sellerId, sellerName |
El vendedor (sellerName es el nombre de su tienda) |
item, itemLabel |
Nombre del objeto y texto visible |
itemKind |
Tipo de objeto (objeto normal, arma, lote…) |
itemMetadata |
Metadatos del objeto (número de serie, durabilidad…), o nil |
category, rarity |
Categoría y rareza detectadas |
quantity |
Unidades |
price / basePrice |
Precio, o precio de salida en las subastas |
buyNowPrice |
Precio de Buy Now, o nil |
listingType |
'fixed', 'auction', 'blind', 'dutch', 'reverse', 'bundle' o 'barter' |
duration |
Duración en minutos |
deliveryMode |
Modo de entrega preferido del vendedor |
deliveryModes |
Todos los modos de entrega que permite el vendedor |
meetupPoint |
ID del punto de recogida, si lo hay |
featured, featuredCost |
Si el vendedor paga por destacarlo y cuánto |
images |
URLs de las fotos |
bundleItems |
Objetos de un lote |
wantedItems, wantsAny |
Lo que pide un anuncio de trueque |
Ejemplos
-- Solo la policía puede vender armasHandlers.CanCreateListing = function(data) if data.category == 'weapons' then local xPlayer = ESX.GetPlayerFromId(data.actorSource) if not xPlayer or xPlayer.job.name ~= 'police' then return false, 'Solo la policía puede vender armas aquí' end end return trueend-- Los objetos legendarios solo pueden ir a subastaHandlers.CanCreateListing = function(data) if data.rarity == 'legendary' and data.listingType ~= 'auction' then return false, 'Los objetos legendarios tienen que subastarse' end return trueendCómo probarlo
- Añade un
printy unreturn false, 'Prueba'como en el consejo de arriba. ensure sl-marketplace.- En el juego, abre el marketplace (
F6o/market) y publica cualquier objeto. - Deberías ver el mensaje Prueba, el objeto se queda en tu inventario y la consola muestra tu línea.
CanBuyListing
Sección titulada «CanBuyListing»Cuándo: un jugador compra un anuncio de precio fijo, un lote, una subasta holandesa o usa Buy Now en una subasta, después de comprobar su dinero y el modo de entrega y antes de mover dinero. También se ejecuta cuando el autor de una subasta inversa acepta la oferta de un vendedor.
Si lo bloqueas: no se cobra nada y el anuncio sigue disponible.
| Campo | Significado |
|---|---|
action |
'buy_listing', o 'accept_reverse_offer' en las subastas inversas |
purchaseType |
'direct', 'buy_now' o 'reverse_offer' |
buyerSource, buyerId, buyerName |
El comprador |
sellerSource, sellerId, sellerName |
El vendedor (sellerSource es nil si está desconectado) |
item, itemLabel, itemMetadata, category, rarity, quantity |
Lo que se compra |
basePrice |
Precio del anuncio que se paga (con el descuento flash aplicado) |
grossPaid |
Total que pagará el comprador, con extras |
deliveryFee, modeSurcharge, npcDeliveryFee, insuranceFee |
Extras (ver campos de dinero) |
deliveryMode |
Modo elegido por el comprador: 'mailbox', 'meetup', 'npc' o 'person' |
listingType |
Tipo de anuncio |
messageId |
Solo con accept_reverse_offer: el mensaje de la oferta |
listingId |
ID del anuncio |
Ejemplos
-- No se puede comprar uno mismo con otra cuenta de la misma licenciaHandlers.CanBuyListing = function(data) local buyer = GetPlayerIdentifierByType(data.buyerSource, 'license') if data.sellerSource and buyer == GetPlayerIdentifierByType(data.sellerSource, 'license') then return false, 'No puedes comprarte a ti mismo' end return trueend-- El repartidor solo a partir del nivel 5Handlers.CanBuyListing = function(data) if data.deliveryMode == 'npc' and exports['your_levels']:GetLevel(data.buyerSource) < 5 then return false, 'La entrega por repartidor se desbloquea en el nivel 5' end return trueendCómo probarlo: bloquéalo con el fragmento de prueba y compra cualquier anuncio. Para probarlo solo, activa Config.Debug = true, crea un anuncio de un vendedor de prueba con /markettest listing mailbox fixed bread 50 y cómpralo (ver Probar los hooks).
CanPlaceBid
Sección titulada «CanPlaceBid»Cuándo: un jugador puja en una subasta (action = 'place_bid') o fija un máximo para la puja automática (action = 'set_proxy_bid'), después de comprobar la cantidad y su dinero.
Si lo bloqueas: la puja no se hace y no se reserva dinero.
| Campo | Significado |
|---|---|
action |
'place_bid' o 'set_proxy_bid' |
bidType |
'manual' o 'proxy' |
bidderSource, bidderId, bidderName |
Quien puja |
sellerSource, sellerId, sellerName |
El vendedor |
item, itemLabel, quantity |
El objeto |
amount |
La puja, o el máximo de la puja automática |
currentBid |
Puja más alta actual |
minimumBid |
Puja mínima válida en este momento |
listingType |
'auction' o 'blind' |
listingId |
ID del anuncio |
Ejemplo
-- Pujas de como mucho $1.000.000Handlers.CanPlaceBid = function(data) if data.amount > 1000000 then return false, 'La puja máxima es de $1.000.000' end return trueendCómo probarlo: bloquéalo con el fragmento de prueba, abre una subasta y puja. Prueba una puja normal y un máximo de puja automática para ver los dos valores de action. Solo: /markettest listing mailbox auction bread 100 crea una subasta de 5 minutos en la que puedes pujar.
CanCancelListing
Sección titulada «CanCancelListing»Cuándo: el vendedor cancela su propio anuncio, antes de marcarlo como cancelado y devolverle el objeto. Las cancelaciones de un administrador desde el panel no pasan por este guard.
Si lo bloqueas: el anuncio sigue activo.
| Campo | Significado |
|---|---|
sellerSource, sellerId, sellerName |
El vendedor |
item, itemLabel, itemMetadata, quantity |
El objeto |
price |
Precio del anuncio |
listingType |
Tipo de anuncio |
listingId |
ID del anuncio |
Ejemplo
-- Una subasta con pujas no se puede cancelarHandlers.CanCancelListing = function(data) if data.listingType == 'auction' then local bids = MySQL.scalar.await('SELECT COUNT(*) FROM marketplace_bids WHERE listing_id = ?', { data.listingId }) if bids and bids > 0 then return false, 'No puedes cancelar una subasta que ya tiene pujas' end end return trueendCómo probarlo: publica algo, bloquea el guard y cancélalo desde tus anuncios en el marketplace.
CanCreateBarterProposal
Sección titulada «CanCreateBarterProposal»Cuándo: un comprador envía una propuesta en un anuncio de trueque (action = 'create_barter_proposal'), o el vendedor responde con una contraoferta (action = 'create_barter_counter'). Se ejecuta antes de retener en depósito los objetos ofrecidos.
Si lo bloqueas: no se retienen objetos ni dinero y no se envía la propuesta.
| Campo | Significado |
|---|---|
action |
'create_barter_proposal' o 'create_barter_counter' |
proposalType |
'proposal' o 'counter' |
recipientSource, recipientId |
Quien recibe la propuesta |
sellerSource, sellerId, sellerName |
El dueño del anuncio de trueque |
item, itemLabel, itemMetadata, quantity |
El objeto anunciado |
offeredItems |
Objetos ofrecidos (vacío en una contraoferta) |
wantedItems |
Objetos que se piden (solo en una contraoferta) |
offerAmount |
Dinero añadido a la oferta |
deliveryMode |
Modo de entrega elegido (nil en una contraoferta) |
messageId |
Propuesta a la que se contraoferta, o nil |
listingId |
ID del anuncio |
Ejemplo
-- Nada de más de $10.000 en efectivo dentro de un truequeHandlers.CanCreateBarterProposal = function(data) if (data.offerAmount or 0) > 10000 then return false, 'Las ofertas de trueque no pueden incluir más de $10.000' end return trueendCómo probarlo: hacen falta dos jugadores. El jugador A publica un anuncio de trueque; el jugador B envía una propuesta (prueba create_barter_proposal); el jugador A responde con una contraoferta (prueba create_barter_counter).
CanAcceptBarterProposal
Sección titulada «CanAcceptBarterProposal»Cuándo: el vendedor acepta una propuesta, o el comprador acepta la contraoferta del vendedor, antes de bloquear el anuncio y los objetos de las dos partes.
Si lo bloqueas: la propuesta sigue abierta.
| Campo | Significado |
|---|---|
acceptanceType |
'proposal' (acepta el vendedor) o 'counter' (el comprador acepta una contraoferta) |
buyerSource, buyerId |
El jugador que hizo la propuesta |
sellerSource, sellerId, sellerName |
El dueño del anuncio |
proposalFromId, proposalToId |
Autor y destinatario del mensaje que se acepta |
item, itemLabel, itemMetadata, quantity |
El objeto anunciado |
offeredItems, wantedItems, offerAmount |
Contenido del trato |
messageId, listingId |
IDs |
Cómo probarlo: dos jugadores. B envía una propuesta al anuncio de trueque de A, bloquea el guard y A la acepta y ve el mensaje. Repite con una contraoferta que acepte B.
CanConfirmInPersonTrade
Sección titulada «CanConfirmInPersonTrade»Cuándo: durante un intercambio en persona (o un trueque que se cierra en persona), cada jugador pulsa el botón para confirmar el intercambio. Se ejecuta una vez por jugador, antes de que cuente su confirmación.
Si lo bloqueas: la confirmación de ese jugador no se registra; el intercambio sigue abierto.
| Campo | Significado |
|---|---|
role |
'buyer' o 'seller': quién está confirmando |
partnerSource, partnerId |
El otro jugador |
buyerSource, buyerId, buyerName |
El comprador |
sellerSource, sellerId, sellerName |
El vendedor |
item, itemLabel, quantity, price |
El trato |
tradeKind |
'person' o 'barter' |
tradeId, listingId |
IDs |
Ejemplo
-- No se puede intercambiar esposadoHandlers.CanConfirmInPersonTrade = function(data) if Player(data.actorSource).state.handcuffed then return false, 'No puedes intercambiar mientras estás esposado' end return trueendCómo probarlo: dos jugadores. B compra el anuncio de A con el modo de entrega en persona, acuerdan un punto de encuentro en el chat, se reúnen allí y pulsan confirmar.
CanRequestRefund
Sección titulada «CanRequestRefund»Cuándo: un comprador abre una solicitud de reembolso de una compra (Config.Refunds.Enabled tiene que ser true y la compra estar dentro de Config.Refunds.WindowDays).
Si lo bloqueas: la solicitud no se crea.
| Campo | Significado |
|---|---|
buyerSource, buyerId, buyerName |
El comprador |
sellerSource, sellerId, sellerName |
El vendedor |
item, itemLabel, quantity |
Lo que se compró |
originalPrice |
Precio pagado |
reason |
Texto escrito por el comprador |
historyId, listingId |
Entrada del historial de compras y anuncio |
Ejemplo
-- Los reembolsos necesitan un motivo de verdadHandlers.CanRequestRefund = function(data) if #(data.reason or '') < 15 then return false, 'Explica el problema (mínimo 15 caracteres)' end return trueendCómo probarlo: compra algo a otro jugador, abre tu historial de compras y pide un reembolso.