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 (IsEntityDead or health <= 0). The server validates reporter proximity and dedupes via a processingDeath flag, then runs the rep loss, loot drop, hit squad, body fade broadcast, and respawn schedule. Server-side health polling is not viable because GetEntityHealth on a server-owned ped with no client owner returns 0.
  • 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