OPX//77
opx_infinityappearance

Server

Last updated on

The appearance contract on the server — read and store faces and clothing, open the fitting room.

local appearance = OPX.Api.Get('appearance')

From server code inside opx_infinity. identifier is a player table, a server id or a citizen id.

GetAppearance

The stored face. Online: reads memory. Offline (citizen id string): reads the database, so it yields.

appearance.GetAppearance(identifier)

Parameters

  • identifier: table | number | string

Returns

  • Result — the snapshot, or nil

SaveAppearance

Validates and stores a face, then tells the client (faceSaved). Yields: call from a thread. The character must be loaded.

appearance.SaveAppearance(identifier, snapshot)

Parameters

  • identifier: table | number | string
  • snapshot: table

Returns

  • Result — the canonical snapshot; errors error.notLoggedIn, appearance.invalid (detail = validation code), appearance.tooLarge
Example
CreateThread(function()
  local saved = appearance.SaveAppearance(source, snapshot)
  if not saved.ok then print(saved.error, saved.detail) end
end)

GetClothing

The stored clothing record. Offline reads yield.

appearance.GetClothing(identifier)

Parameters

  • identifier: table | number | string

Returns

  • Result — the record, false when nothing is stored, nil when the stored row could not be read; error clothing.unreadable

SaveClothing

Stores a clothing record. Yields. The character must be loaded. Does not check a fitting-room grant.

appearance.SaveClothing(identifier, clothing)

Parameters

  • identifier: table | number | string
  • clothing: table — a clothing record

Returns

  • Result — errors error.notLoggedIn, clothing.invalid (detail = code), error.unavailable (clothing_unavailable: stored record unreadable this session), clothing.tooLarge

OpenWardrobe

Asks the player's client to open the fitting room and grants a clothing save. The caller checks permission. With charge, the room is priced: the server bills the save, not the client (see AllowClothingSave).

appearance.OpenWardrobe(playerId, options)

Parameters

  • playerId: number
  • options?: table
    • owner?: string — only logged
    • charge?: function(source, slots) — see below. Without it the room is free.

Returns

  • boolean — true: the ask went out (not that a room opened)
  • string? — 'invalid_player' on false

AllowClothingSave

Lets the player's client save clothing. A save with no grant is refused clothing.noFittingRoom. What options holds picks the kind of grant:

appearance.AllowClothingSave(playerId, owner, options)

Parameters

  • playerId: number
  • owner: string — only logged
  • options?: table
    • nothing: open grant. Any save for 10 minutes, free (the join's room, staff, a clothing store).
    • charge: priced grant. function(source, slots) is called with the slots the server finds changed (the save against the stored record, outfit overrides included), before anything is written. Answer true[, nil, undo] to let it through (undo refunds if the write then fails) or false, code to refuse the save; the client puts the stored look back.
    • wear = { Slot = 'Items.Record' | false }: look grant. One free save of exactly that look on top of what is stored, for a door that dresses without a room (a bought uniform, a saved outfit). At most 8 are held per player.
    • bill: with wear, the look's price. function(source) answers like charge and runs only on the save that keeps the look (a Cancel costs nothing). The slots it covers are not billed again by a priced room. If any bill on a save is refused, the save is refused and every charge of that save is refunded through its undo; the grant stays usable.
  • A priced or open grant lasts 10 minutes; while the room is open the client renews it every 2 minutes (opx:net:appearance:roomAlive), up to 30 minutes after the grant.

Returns

  • boolean
  • string? — 'invalid_player', 'invalid_slot', 'invalid_item' on false
Example — a priced room
appearance.OpenWardrobe(playerId, { owner = 'mytailor', charge = function(source, slots)
  local paid = character.RemoveMoney(source, 'EDDIES', 50 * #slots, 'tailor')
  if not paid then return false, 'clothing.unpaid' end
  return true, nil, function() character.AddMoney(source, 'EDDIES', 50 * #slots, 'refund') end
end })

A look already paid for inside the room (a uniform bought from a shop) is not billed again by the room's save.

PricedRoom

The owner of the priced room (a charge grant) the player holds right now, or nil. A read of the unexpired grant, not of the screen. Shops use it to accept a share code only inside their own room (shops:<key>).

appearance.PricedRoom(playerId)

Parameters

  • playerId: integer

Returns

  • string? — the owner the room was opened under
Example
if appearance.PricedRoom(source) ~= 'shops:' .. shopKey then return refuse('shops.redeemInRoom') end

The clothing record

{
  schemaVersion = 1,
  equipment = { Head = 'Items.…' or false, Face, InnerChest, OuterChest, Legs, Feet,
                Outfit, UnderwearTop, UnderwearBottom },
  wardrobe = { active = 0..6 or nil, outfits = { ['0'] = { Head = 'Items.…', … }, … } },
}
  • Every item matches ^Items%.[%w_%.%-]+$ and is at most 160 bytes.
  • Outfits are indexed 0 to 6 and may override the seven visible slots only (not underwear).

On this page