Developer

3 min readUpdated 2 weeks ago

Overview

ML Tattoos exposes server exports for the uses economy, a client export to open the shop, and override hooks in open/. Mutating use exports are restricted to the resources listed in Config.AllowedConsumerResources; read exports are open.

Server Exports

All under exports.ml_tattoos:<Name>(...). identifier is the player's framework id; ctype is 'solo', 'faction' or 'ytd'.

  • GrantTattooCredit(identifier, ctype, amount, reason?): Grants uses to a player (capped per type). Caller must be an allowed resource. Returns ok, granted
  • GetTattooCredits(identifier): Returns the player's balances: { solo, faction, ytd, total_granted, total_consumed }. Open to any resource
  • ConsumeTattooCredit(identifier, ctype): Consumes one use of a type. Caller must be an allowed resource. Returns ok, reason?
  • RefundTattooCredit(identifier, ctype, reason?): Refunds one use of a type (capped). Caller must be an allowed resource

Two server exports are reserved for the Tebex hub and are called only by the ml_tebex dispatcher: OnTebexPackage(ev) (grants the package's uses) and OnTebexRefund(ev) (reverses a refunded transaction, returning { status, detail }). You do not call these yourself.

Client Exports

  • openShop(opts): Opens the tattoo shop. opts may carry mode ('shop' / 'item'), a limit, and preset filters (zones, categories, hashes, allowCustom, customOnly). Returns false if the shop is already open

open/ Handlers

Override these in the open/ files. They are not escrowed.

Client

open/client.lua
1function OpenClient.BeforeOpenShop(shopData)
2    return true        -- return false to block the shop from opening
3end
4
5function OpenClient.OnTattooPurchased(tattoos)
6    -- runs client-side after a successful checkout
7end

Server

open/server.lua
1function OpenServer.OnCheckout(src, tattoos, totalCost)
2    -- runs after a successful checkout (src, full tattoo list, money charged)
3end
4
5function OpenServer.OnCustomApproved(src, data)
6    return true        -- return false to block a custom tattoo submission
7end

Shared

open/shared.lua
1function OpenShared.CanBuyTattoo(tattooHash, tattooDlc)
2    return true        -- return false to block a specific tattoo
3end

Examples

Add your resource to Config.AllowedConsumerResources first, then:

lua
1-- e.g. inside a coin-donator redemption, server-side:
2local identifier = exports.ml_bridge:GetPlayerId(src)
3local ok, granted = exports.ml_tattoos:GrantTattooCredit(identifier, 'solo', 1, 'coin_redeem')
4if ok then
5    print(('granted %d solo use(s)'):format(granted))
6end
lua
1local identifier = exports.ml_bridge:GetPlayerId(src)
2local bal = exports.ml_tattoos:GetTattooCredits(identifier)
3print(('solo=%d faction=%d ytd=%d'):format(bal.solo, bal.faction, bal.ytd))
open/shared.lua
1function OpenShared.CanBuyTattoo(tattooHash, tattooDlc)
2    -- block one DLC pack entirely:
3    if tattooDlc == 'mpluxe2_overlays' then return false end
4    return true
5end

Client examples use ox_lib cache.* for player state.

lua
1-- open the full shop:
2exports.ml_tattoos:openShop({ mode = 'shop' })
3
4-- open filtered to one zone, one tattoo:
5exports.ml_tattoos:openShop({
6    mode = 'item', limit = 1,
7    zones = { 'ZONE_LEFT_ARM' }, allowCustom = false,
8})
open/server.lua
1function OpenServer.OnCheckout(src, tattoos, totalCost)
2    if totalCost > 0 then
3        print(('player %d paid %d for %d tattoo(s)'):format(src, totalCost, #tattoos))
4    end
5end