Exports & Events

Exports, callbacks, state bags, and events for integrating with oxide-blackmarket.

Reference for integrating with oxide-blackmarket.

oxide-blackmarket uses Callback = olink.callback internally. Client callback usage assumes your resource already has access to the same o-link callback object.

Server Exports

Active dealers (multi-dealer)

Several dealers are active at once. These return the live set.

local dealers = exports['oxide-blackmarket']:GetActiveDealers()
-- dealers = { { id, netId, coords, spawnIndex }, ... }

local nearest = exports['oxide-blackmarket']:GetNearestDealer(vector3(x, y, z))
-- nearest = { id, netId, coords, distance } | nil

Returns:

  • GetActiveDealers() -> table — a list of { id = number, netId = number, coords = vector3, spawnIndex = number }, one per currently active dealer (empty list if none)
  • GetNearestDealer(coords) -> table|nil — the active dealer closest to coords as { id, netId, coords, distance }, or nil if none are active / coords is missing

Dealer state (single-dealer, back-compat)

These predate the multi-dealer system and resolve to the first active dealer (use GetActiveDealers / GetNearestDealer for full awareness).

local netId = exports['oxide-blackmarket']:GetDealerNetId()
local coords = exports['oxide-blackmarket']:GetDealerCoords()
local spawnCoords = exports['oxide-blackmarket']:GetDealerSpawnCoords()
local active = exports['oxide-blackmarket']:IsDealerActive()
local valid = exports['oxide-blackmarket']:IsDealerValid()

Returns:

  • GetDealerNetId() -> number|nil — first active dealer's network id
  • GetDealerCoords() -> vector3|nil — first active dealer's live coords
  • GetDealerSpawnCoords() -> vector3|nil — first active dealer's configured spawn coords
  • IsDealerActive() -> boolean — true if at least one dealer is active
  • IsDealerValid() -> boolean — true if at least one dealer is active

Stock

Each dealer keeps its own stock pool, tracked per catalog entry. An entry id has the form 'tier:index' (e.g. '3:6' = tier 3, sixth entry). Entries can share an item name (e.g. the printer_usb blueprints) while keeping separate stock pools. The stock exports aggregate across all active dealers by item name.

local stock = exports['oxide-blackmarket']:GetDealerStock()
local qty = exports['oxide-blackmarket']:GetItemStock('weapon_pistol')
local ok = exports['oxide-blackmarket']:UpdateDealerStock('weapon_pistol', -1)

Returns:

  • GetDealerStock() -> table{ [itemName] = count }, summed across every active dealer
  • GetItemStock(itemNameOrEntryId) -> number — an item name returns the total across all dealers and all entries with that name; an entry id returns that entry's total across all dealers
  • UpdateDealerStock(itemNameOrEntryId, amount) -> boolean — acts on the first active dealer. An entry id updates that entry directly; with an item name, positive amounts restock the first matching entry and negative amounts drain matching entries in catalog order

Reputation

local rep = exports['oxide-blackmarket']:GetPlayerReputation(source)
local ok1 = exports['oxide-blackmarket']:SetPlayerReputation(source, 500)
local ok2 = exports['oxide-blackmarket']:AddPlayerReputation(source, 50)
local ok3 = exports['oxide-blackmarket']:RemovePlayerReputation(source, 100)
local ok4 = exports['oxide-blackmarket']:AddReputationWithNotify(source, 50)
local ok5 = exports['oxide-blackmarket']:RemoveReputationWithNotify(source, 100)

Returns:

  • GetPlayerReputation(source) -> number
  • SetPlayerReputation(source, amount) -> boolean
  • AddPlayerReputation(source, amount) -> boolean
  • RemovePlayerReputation(source, amount) -> boolean
  • AddReputationWithNotify(source, amount) -> boolean
  • RemoveReputationWithNotify(source, amount) -> boolean

Settings

Configuration is stored in the oxide_settings database table (see Configuration). These read and write it at runtime, keyed by the top-level config name without the Config. prefix.

local dealerCount = exports['oxide-blackmarket']:GetSetting('DealerCount')
local ok = exports['oxide-blackmarket']:SetSetting('DealerCount', 5)

Returns:

  • GetSetting(key) -> any — the current (database-backed) value of that setting
  • SetSetting(key, value) -> boolean — updates the setting live: applies it on the server immediately, saves it to the database, and syncs it to clients. The change persists across restarts. (Editing the database row directly instead needs a resource restart to take effect.)

Client Exports

Dealer and menu state

local entity = exports['oxide-blackmarket']:GetDealerEntity()
local active = exports['oxide-blackmarket']:IsDealerActive()
local inMenu = exports['oxide-blackmarket']:IsInMenu()
exports['oxide-blackmarket']:SetInMenu(true)

Returns:

  • GetDealerEntity() -> number|nil — the entity handle of the nearest streamed-in dealer ped
  • IsDealerActive() -> boolean — true if any dealer ped is currently streamed in
  • IsInMenu() -> boolean — true while a shop NUI is open

UI

exports['oxide-blackmarket']:OpenShopUI(shopData)
exports['oxide-blackmarket']:CloseShopUI()
exports['oxide-blackmarket']:UpdateShopUI('updateStock', { id = '2:2', item = 'weapon_pistol', stock = 5 })

local isShopOpen = exports['oxide-blackmarket']:IsShopOpen()
local isNuiOpen = exports['oxide-blackmarket']:IsNUIOpen()

Returns:

  • IsShopOpen() -> boolean
  • IsNUIOpen() -> boolean

Player state

local loggedIn = exports['oxide-blackmarket']:IsLoggedIn()
local isPolice = exports['oxide-blackmarket']:IsPlayerPolice()

Returns:

  • IsLoggedIn() -> boolean
  • IsPlayerPolice() -> boolean

Server Callbacks

oxide-blackmarket:server:GetReputation

Callback.Trigger('oxide-blackmarket:server:GetReputation', function(data)
    print(data.rep, data.tier, data.tierName)
end)

Return shape:

{
    rep = number,
    tier = number,
    tierName = string
}

oxide-blackmarket:server:IsPlayerPolice

Callback.Trigger('oxide-blackmarket:server:IsPlayerPolice', function(isPolice)
    print(isPolice)
end)

Returns boolean.

State Bags

oxide-blackmarket:dealer

Replicated entity state bag set on each active dealer ped. Its value is the dealer's id (a small number) while the ped is a live dealer; it is cleared (set to nil) when the ped dies or is deleted. Clients identify each dealer via this bag rather than a captured entity reference, since out-of-scope clients only resolve the entity when it streams into their scope. With multiple dealers active, the id distinguishes one from another.

-- Client: check if a ped is a dealer, and which one
local dealerId = Entity(entity).state['oxide-blackmarket:dealer']
if dealerId then
    -- this ped is dealer `dealerId`
end

-- React to stream-in / stream-out
AddStateBagChangeHandler('oxide-blackmarket:dealer', nil, function(bagName, _, value)
    local entity = GetEntityFromStateBagName(bagName)
    -- value == dealerId on spawn/stream-in, nil on despawn/stream-out/death
end)

Client Events

Dealer

  • oxide-blackmarket:client:SpawnHitSquad(coords)
  • oxide-blackmarket:client:DealerBodyFade(netId) — server-driven body fade triggered by the death flow

Reputation

  • oxide-blackmarket:client:TierChanged(newTier, newTierName)
  • oxide-blackmarket:client:TierUp(tierName)
  • oxide-blackmarket:client:TierDown(tierName)
  • oxide-blackmarket:client:ReputationLost()

Shop

  • oxide-blackmarket:client:OpenShop(shopData)
  • oxide-blackmarket:client:TransactionResult(success, message, updateData)

shopData contains:

{
    playerRep = number,
    playerTier = number,
    tierName = string,
    playerCash = number,
    buyInventory = table,   -- entries: { id, item, label, price, stock, info, category }
    sellInventory = table,  -- entries: { item, label, count, sellPrice, repGain, category }
    enableTiers = boolean,
    dealerNetId = number,   -- which dealer this shop is open against; echoed back on buy/sell
}

buyInventory[n].id is the catalog entry id ('tier:index') used to identify the entry in purchases and stock updates. sellInventory[n].sellPrice is the current market price (after saturation). The category on each entry is the heading it's grouped under in the UI.

Loot

  • oxide-blackmarket:client:SpawnLoot(coords, lootId)
  • oxide-blackmarket:client:RemoveLootBag(lootId)
  • oxide-blackmarket:client:LootCollected()

Pager

  • oxide-blackmarket:client:PagerSending(duration) — plays the texting animation and progress bar for duration ms while the reply is pending
  • oxide-blackmarket:client:PagerResult(success, payload)

Pager result behavior:

  • On success, payload is a table { detail = string, waypoint = boolean, coords = vector3 }. detail is 'area', 'street', or 'exact' (set by the player's tier via Config.PagerIntel); the client composes the anonymous tip email from it and drops a map waypoint when waypoint is true.
  • On failure, payload is a message string.

Server Events

Every dealer-facing server event carries the dealer's netId so the server knows which of the active dealers the client means.

Dealer and arrest

TriggerServerEvent('oxide-blackmarket:server:DealerKilled', netId)
TriggerServerEvent('oxide-blackmarket:server:DealerSurrendered', netId, state)
TriggerServerEvent('oxide-blackmarket:server:DeliverDealer', netId, dropOffIndex)

Notes:

  • DealerKilled is fired by the in-scope client that owns the ped at the moment of death. The server validates reporter proximity, dedupes duplicate reports, then runs the rep loss, loot drop, hit squad, body fade broadcast, and respawn schedule.
  • DealerSurrendered(netId, state) reports whether the dealer's hands are up (state = true) or he's broken free (state = false). It controls the reduced police society payout if the dealer is killed while surrendered.
  • DeliverDealer(netId, dropOffIndex) finalizes a transport arrest (it replaces the old instant ArrestDealer event). The client fires it when the officer's vehicle reaches a drop-off; the server re-validates that the caller is police, is within Config.DropOffRadius of Config.ArrestDropOffs[dropOffIndex], and has the dealer present, then pays the reward (or funds the oxide-police department treasury) and despawns the dealer.

Shop

TriggerServerEvent('oxide-blackmarket:server:RequestShop', netId)
TriggerServerEvent('oxide-blackmarket:server:Purchase', netId, entryId, quantity)
TriggerServerEvent('oxide-blackmarket:server:Sell', netId, itemName, quantity)

Notes:

  • All three are scoped to the dealer identified by netId, so purchases and sales draw from and affect that specific dealer's stock.
  • Purchase takes the catalog entry id from shopData.buyInventory ('tier:index'). A plain item name is also accepted and resolves to the first catalog entry with that name within the player's tier — fine for unique item names, ambiguous for duplicates like printer_usb.
  • Sell prices at the current market rate and is subject to the optional daily sell cap.

Loot

TriggerServerEvent('oxide-blackmarket:server:CollectLoot', lootId)

Integration Examples

Grant blackmarket rep from another resource

RegisterNetEvent('my-heist:server:completed', function()
    local src = source
    exports['oxide-blackmarket']:AddReputationWithNotify(src, 100)
end)

Check whether a dealer is currently up

local function getDealerLocation()
    if not exports['oxide-blackmarket']:IsDealerValid() then
        return nil
    end

    return exports['oxide-blackmarket']:GetDealerCoords()
end

Use current dealer stock in another script

local function canBuyFromDealer(itemName, quantity)
    return exports['oxide-blackmarket']:GetItemStock(itemName) >= quantity
end

Next Steps