Creators
Last updated on
How your own Open77 resource talks to opx_infinity — exports, events, who may call, and when to await.
For a separate resource (your own job, shop or heist). opx_infinity gives you three doors and nothing else:
| Door | Side | Use it to |
|---|---|---|
| Server exports | server | read a player, the roster, the jobs, the catalogue, a bag, vehicles and doors; change money, jobs, gangs, duty, your own metadata (online or not), items, stashes, chat, keys, vehicles, doors; register items, use handlers and crafting benches; a toast, a bar the server judges; revive |
| Client exports | client | open a menu, form or panel, show a toast, run a progress bar, put rows on the eye and hints on the key strip, open a crafting bench, play an animation, read the local player; subscribe to OPX client events |
| Public server events | server | react when a character loads, money moves, an item is used… |
The modules' own contracts (OPX.Api.Get) stay inside opx_infinity. If you need more, write a module instead.
Every export answers one table
{ ok = true, value = <answer> } -- value may be nil
{ ok = false, error = '<code>' } -- a stable code you can branch onAn export never raises. Always test answer.ok == true.
| Code | Meaning |
|---|---|
export.callerDenied | Your resource is not allowed to make this call (Who may call). |
export.badArgument | An argument could not be read (wrong type, bad player id, bad citizen id, name too long…). |
export.booting | The server has not finished starting. Try again later. |
export.badValue | SetMetadata: the value is not plain data. |
export.tooLarge | SetMetadata: over a size or key-count limit. |
export.notSubscribable | Subscribe: that event is not on the list. |
export.ownerTaken | Eye and key strip: your resource is named like a module of OPX, whose rows it would share. |
export.usableTaken | RegisterUsableItem: another owner already handles that item's use. |
export.duplicate | Notify: the same toast was just shown to that player. |
export.mustAwait | This call can reach the database. Call it with Open77.exports.call(...) and :await() (below). |
error.unavailable | The module behind the export is not running, or the call failed. |
error.notLoggedIn | That player has no character loaded. |
| other codes | Passed through from the module: money.insufficient, stash_cap, vehicle.busy… Each export lists its own. |
A code is not a sentence. The export.* codes have English and French text in opx_infinity, but your resource shows its own text: translate the codes you show. See Error codes.
Who may call
The caller is the resource name the host reports (GetInvokingResource), never an argument. The operator decides:
| Setting | File | Default | Controls |
|---|---|---|---|
SERVER.EXPORTS.READ | config/server.lua | '*' (everyone) | server exports that only read |
SERVER.EXPORTS.WRITERS | config/server.lua | {} (nobody) | server exports that change something |
CLIENT.EXPORTS.CALLERS | config/client.lua | '*' (everyone) | client exports |
A list is '*', a set { my_shop = true } or an array { 'my_shop' }. When a server export refuses your resource, the journal prints the line to add, once per resource and export:
[exports] my_shop was refused AddMoney; to admit it, add `my_shop = true` to EXPORTS.WRITERS in config/server.luaWriters are trusted with money
A resource in WRITERS can create money and items. Every write is audited with the caller's name (event=export.<Name>), and money reasons are written ext:<resource>:<reason>.
Sync or await?
-- synchronous: answers at once; fails if the export has to wait
local answer = exports.opx_infinity:GetPlayerData(playerId)
-- asynchronous: returns a promise; call it from a thread and await it
local pending, why = Open77.exports.call('opx_infinity', 'AddMoney', playerId, 'EDDIES', 250, 'tip')
local answer = pending and pending:await()| Sync is fine | Must use Open77.exports.call(...):await() |
|---|---|
GetVersion, GetPlayerData, GetPlayerByCitizenId, GetPlayers, GetJobs, GetGangs, IsStaff, GetMoney, HasJob, HasGang, GetJob, GetGang, GetItem, GetItems, IsDown, the door reads, GetMetadata / GetInventory / CanCarryItem / HasKeys for a player id, and the memory-only writes SetDuty, SetMetadata (player id), Revive, RegisterItem, RegisterUsableItem, UnregisterUsableItem, Notify, StartProgress, StopProgress, RegisterCraftingBench, UnregisterCraftingBench | AddMoneyOffline, SetJob, SetGang, RemoveJob, RemoveGang, GetNeeds, AddNeeds, SetNeeds, the stash exports and OpenStash, RevokeKeys, RevokeAllKeys, GiveKeys, SetVehicleState, GetVehicle, GetOwnedVehicles, AddVehicle, the door writes, and the item, metadata and key exports when target is a citizen id. The promise form for every other write is a safe habit. |
- A call that may reach the database, made synchronously, answers
export.mustAwaitbefore touching anything, so it is safe to retry with the promise form. awaitneeds a thread:CreateThread, an event handler or a command handler, never file scope.
Your manifest
dependency "opx_infinity" -- optional, recommended- A declared dependency starts
opx_infinityfirst and stops your resource with it. It grants nothing: the allowlist still decides. - Publishing and calling exports needs no permission.
- A client resource that wants answers must publish a reply export: see Client exports.