Ir al contenido

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

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 armas
Handlers.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 true
end
-- Los objetos legendarios solo pueden ir a subasta
Handlers.CanCreateListing = function(data)
if data.rarity == 'legendary' and data.listingType ~= 'auction' then
return false, 'Los objetos legendarios tienen que subastarse'
end
return true
end

Cómo probarlo

  1. Añade un print y un return false, 'Prueba' como en el consejo de arriba.
  2. ensure sl-marketplace.
  3. En el juego, abre el marketplace (F6 o /market) y publica cualquier objeto.
  4. Deberías ver el mensaje Prueba, el objeto se queda en tu inventario y la consola muestra tu línea.

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 licencia
Handlers.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 true
end
-- El repartidor solo a partir del nivel 5
Handlers.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 true
end

Có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).

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.000
Handlers.CanPlaceBid = function(data)
if data.amount > 1000000 then
return false, 'La puja máxima es de $1.000.000'
end
return true
end

Có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.

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 cancelar
Handlers.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 true
end

Cómo probarlo: publica algo, bloquea el guard y cancélalo desde tus anuncios en el marketplace.

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 trueque
Handlers.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 true
end

Có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).

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.

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 esposado
Handlers.CanConfirmInPersonTrade = function(data)
if Player(data.actorSource).state.handcuffed then
return false, 'No puedes intercambiar mientras estás esposado'
end
return true
end

Có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.

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 verdad
Handlers.CanRequestRefund = function(data)
if #(data.reason or '') < 15 then
return false, 'Explica el problema (mínimo 15 caracteres)'
end
return true
end

Cómo probarlo: compra algo a otro jugador, abre tu historial de compras y pide un reembolso.