OPX//77
CreatorsClient exports

Client exports

Last updated on

Put OPX menus, forms, panels, toasts, progress bars, eye rows, key hints and animations on the local player's screen from your own client script, read the local player, and hear OPX client events.

Defined in core/client/exports.lua. They answer { ok, value | error } like the server exports. Callers allowed: CLIENT.EXPORTS.CALLERS ('*', everyone, by default). None of them yields, so the synchronous form is fine:

local answer = exports.opx_infinity:ShowToast({ message = 'Order ready', kind = 'success' })

Everything here acts on this player's screen only. The server must still check anything that matters.

Getting answers back: the reply export

A function cannot be passed to another resource, and a client TriggerEvent does not leave opx_infinity, so an answer is delivered by calling an export of yours. Publish this once:

my_shop/client/main.lua
exports('OnOpxEvent', function(event, payload)
	TriggerEvent(event, payload)   -- re-raise it on your own resource's bus
end)

Every answer then arrives in your resource as an ordinary event:

Event (on your bus)Sent afterPayload
opx:on:menu:actionevery action on a menu you openedthe menu's action payload
opx:on:form:answera form you opened is submitted or cancelledthe form's answer
opx:on:panel:actionevery action on a panel you openedthe panel's action payload, owner your resource
opx:on:progress:donea bar you started ends{ owner, label, ending, finished }; only finished = true means the action completed
opx:on:animations:resultthe server's verdict on an animation you asked for{ requestId, action, ok, error?, animation?, variant?, playbackId?, ... }
  • In each payload owner is your resource name.
  • The export is CLIENT.EXPORTS.REPLY (OnOpxEvent) unless the call names another with reply (letters, digits, _, max 64).
  • If your resource does not publish it, the answer is lost and the client log says [exports] <you> did not take <event> through its OnOpxEvent export: ....

The exports

ExportDoes
OpenMenuOpens a menu
UpdateMenuPatches your open menu
CloseMenuCloses your menu
OpenFormOpens a form
CloseFormCancels your form
ShowToastShows a toast
DismissToastRemoves your toast
StartProgressRuns a progress bar
StopProgressStops your bar
PlayAnimationAsks for an emote on the local player
StopAnimationStops your animation
SubscribeForwards an OPX client event to you
UnsubscribeStops forwarding it
OpenPanelOpens the side panel
UpdatePanel, AppendPanel, ClosePanelPatch, fill and close your panel
AddTargetPuts rows on the target eye
UpdateTarget, RemoveTarget, ClearTargetsChange and remove your rows
ShowPromptPuts key hints on the key strip
UpdatePrompt, HidePrompt, HideAllPromptsChange and take down your hints
OpenCraftingBenchOpens one of your crafting benches
GetPlayerData, GetItemCount, HasItem, GetItem, IsDown, GetNeedsRead the local player

Errors common to all: export.callerDenied, export.badArgument, error.unavailable (the module is not running), stale_handle, and the codes of the module behind the export (for example menu_busy, form_busy, progress_busy, or a spec error such as invalid_items).

Subscribing to OPX client events

Your resource cannot hear opx_infinity's client events directly. Subscribe forwards one to your reply export every time it is raised, until you unsubscribe or your resource stops. Each arrives as (event, payload) with one table:

EventPayload
opx:on:character:loadedthe character's PlayerData without metadata
opx:on:character:unloaded{}
opx:on:character:changedPlayerData without metadata
opx:on:character:money{ moneyType, amount, action, balance } (one table, not four arguments)
opx:on:character:jobthe primary job table
opx:on:character:gangthe primary gang table
opx:on:downed:changed{ down, waiting }
opx:on:inventory:changed{ inventory, changes } (see inventory)
opx:on:inventory:used{ name, slot, label, close, status?, animation? }
opx:on:inventory:opened{}
opx:on:inventory:closed{}
opx:on:needs:changed{ values, changed, source, citizenId, ready } — source is loaded, the server's reason (decay, use, staff, ext:<resource>…) or unloaded
opx:on:progress:state{ open, owner?, label? }. owner is your resource name for your bar; a bar drawn by another resource has no owner.

No other event can be subscribed to (export.notSubscribable).

my_hud/client.lua
exports('OnOpxEvent', function(event, payload) TriggerEvent(event, payload) end)

CreateThread(function()
	exports.opx_infinity:Subscribe('opx:on:character:money')
end)

AddEventHandler('opx:on:character:money', function(p)
	print(('balance %s is now %d'):format(p.moneyType, p.balance))
end)

Ownership

  • Your screens are kept under the owner ext:<your resource>. You can update, close or stop only what you opened; you never take over another owner's menu or form.
  • When your resource stops, OPX closes your menus (reason owner_stopped), forms and panel, stops your bar and your animation, removes your eye rows and key hints, and drops your subscriptions.
  • Eye rows and key hints are kept under your resource name (the eye watches its generation and drops them on a reload). A resource named like a module of OPX (target, inventory, ...) is refused them: export.ownerTaken.

Example: a shop menu

my_shop/client/main.lua
exports('OnOpxEvent', function(event, payload) TriggerEvent(event, payload) end)

local function openShop()
	local answer = exports.opx_infinity:OpenMenu({
		title = 'NIGHT MARKET',
		closeOnSelect = true,
		items = {
			{ id = 'water', label = 'Water', value = '5 €$' },
			{ id = 'burrito', label = 'Burrito', value = '12 €$' },
		},
	})
	if not answer.ok then print('menu refused: ' .. answer.error) end
end

AddEventHandler('opx:on:menu:action', function(p)
	if p.action == 'select' and p.itemId then
		-- ask YOUR server to sell it; the server checks and charges
		TriggerServerEvent('my_shop:buy', p.itemId)
	end
end)

On the server, my_shop:buy re-checks the item and the player, then charges with RemoveMoney and gives with AddItem, both awaited.

On this page