AI Core

Lua-only helpers

aiLib.playerData, aiLib.online, aiLib.isAdmin and the aiLib.menu table: the aiLib functions whose Lua form differs from the TypeScript lib.

Most aiLib functions are the Lua twin of a TypeScript function and are documented next to it, with a Lua tab (see Every aiLib function). The ones on this page have a different name, shape or behaviour in Lua, so they are documented here.

Client: player data

aiLib.playerData.get

The last value ai-core sent for one key of the local player's data. When nothing has arrived since your resource started, it asks ai-core once (GetPlayerData) and keeps the answer.

Side: Client · Waits: no

Lua
local value = aiLib.playerData.get(key)

TypeScript: the same data through getPlayerData(key) (see Membership and player data).

Parameters

Prop

Type

Returns

The value: a table for character, money ({ cash = 500, bank = 5000 }), jobs, job, gangs and metadata; a string or nil for duty and activeGang. nil while no character is loaded or ai-core is not running.

Example

client.lua
RegisterCommand('cash', function()
    local money = aiLib.playerData.get('money') or {}
    aiLib.notify(('Cash: $%s'):format(aiLib.numbers.groupDigits(money.cash or 0)), 'info')
end, false)

aiLib.playerData.onChange

Runs a function each time ai-core pushes a new value of one key for the local player (a job change, money, going on duty). For money and metadata, ai-core sends only the changed entries; aiLib merges them, so your handler always gets the whole table.

Side: Client · Waits: no

Lua
aiLib.playerData.onChange(key, function(value) end)

TypeScript: onPlayerData(key, handler) (see Membership and player data).

Parameters

Prop

Type

Returns

Nothing. There is no way to remove the handler; it goes away with your resource.

Example

client.lua
aiLib.playerData.onChange('duty', function(job)
    if job == 'police' then
        aiLib.notify('You are now on duty', 'success')
    end
end)

Server: online players

aiLib.online reads loaded players through ai-core's exports, one export call per read. It is meant for lookups when an action happens (a command, a callback), not for every tick. The TypeScript online keeps a local mirror with more readers and different return shapes (see Players).

aiLib.online.character

The loaded character of a player.

Side: Server · Waits: no

Lua
local character = aiLib.online.character(src)

Parameters

Prop

Type

Returns

A CharacterInfo table (id, citizenid, firstname, lastname, birthdate, sex, nationality...), or nil when the player has no character loaded.

Example

server.lua
aiLib.addCommand('whoami', { help = 'Shows your character id' }, function(source)
    local character = aiLib.online.character(source)
    if not character then return end
    aiLib.notify(source, ('Character #%d (%s)'):format(character.id, character.citizenid), 'info')
end)

aiLib.online.name

The character's "Firstname Lastname", else the player's FiveM name.

Side: Server · Waits: no

Lua
local name = aiLib.online.name(src, fallback)

TypeScript: online.name(source, { fallback }) takes an options table instead of a boolean.

Parameters

Prop

Type

Returns

string: the character name, else the FiveM name (or "" with fallback = false, or when the player is gone).

Example

server.lua
print(('%s opened the shop'):format(aiLib.online.name(source)))

aiLib.online.jobs

Every job of a player, on duty or not.

Side: Server · Waits: no

Lua
local jobs = aiLib.online.jobs(src)

TypeScript: online.jobs(source) returns only the job names; Lua returns the membership tables.

Parameters

Prop

Type

Returns

A list of JobMembership tables ({ job = 'police', grade = 2 }). An empty list when the player has none or no character.

Example

server.lua
for _, membership in ipairs(aiLib.online.jobs(source)) do
    print(membership.job, membership.grade)
end

aiLib.online.gangs

Every gang of a player.

Side: Server · Waits: no

Lua
local gangs = aiLib.online.gangs(src)

TypeScript: online.gangs(source) returns only the gang names.

Parameters

Prop

Type

Returns

A list of GangMembership tables ({ gang = 'ballas', grade = 0 }), or an empty list.

Example

server.lua
local inAGang = #aiLib.online.gangs(source) > 0

aiLib.online.jobGrade

A player's grade in one job.

Side: Server · Waits: no

Lua
local grade = aiLib.online.jobGrade(src, job)

Parameters

Prop

Type

Returns

integer (0 = the lowest grade), or nil when the player is not in that job.

Example

server.lua
local grade = aiLib.online.jobGrade(source, 'police')
if not grade or grade < 2 then return aiLib.fail('NOT_ALLOWED') end

aiLib.online.gangGrade

A player's grade in one gang.

Side: Server · Waits: no

Lua
local grade = aiLib.online.gangGrade(src, gang)

Parameters

Prop

Type

Returns

integer, or nil when the player is not in that gang.

Example

server.lua
local isBoss = (aiLib.online.gangGrade(source, 'ballas') or -1) >= 3

aiLib.online.dutyJob

The job a player is on duty for.

Side: Server · Waits: no

Lua
local job = aiLib.online.dutyJob(src)

Parameters

Prop

Type

Returns

The job name, or nil when off duty.

Example

server.lua
if aiLib.online.dutyJob(source) ~= 'police' then return aiLib.fail('NOT_ON_DUTY') end

aiLib.online.sources

The server ids of every player with a loaded character.

Side: Server · Waits: no

Lua
local players = aiLib.online.sources()

Returns

A list of server ids (empty when nobody is loaded or ai-core is not running).

Example

server.lua
for _, src in ipairs(aiLib.online.sources()) do
    if aiLib.online.dutyJob(src) == 'police' then
        aiLib.notify(src, 'Robbery in progress', 'warning')
    end
end

Each call is an export call to ai-core. In a loop over every player, read what you need once per player, and never run it every frame. For "how many on duty", read the ai:duty:<job> global state (GlobalState['ai:duty:police']) instead.

Server: permissions

aiLib.isAdmin

Whether a player is an ai-core admin (ACE ai.admin), or has the ACE you name.

Side: Server · Waits: no

Lua
local allowed = aiLib.isAdmin(src, ace)

TypeScript: isAdmin(ace?) is a guard for onClientCallback / onClientEvent instead (see Client input). In Lua, gate a callback with its ace option, or call aiLib.isAdmin yourself.

Parameters

Prop

Type

Returns

true when the player has the ACE, else false.

Example

server.lua
RegisterCommand('fixall', function(source)
    if not aiLib.isAdmin(source) then return end
    -- admin-only work here
end, false)

Client: the menu table

aiLib.menu

The dialogs live on one table. The top-level names are the same functions, kept for short calls:

On aiLib.menuSame asWaitsAnswers
menu.show(request, listeners?)(only here)yesThe picked item id, '__back' for a sub menu's back, or nil when closed.
menu.input(request)aiLib.input(request)yesA table of the values by field name, or nil when closed.
menu.itemPicker(request)aiLib.itemPicker(request)yesA list of { name, count }, or nil. Admin tools only.
menu.skillCheck(difficulty, keys?)aiLib.skillCheck(difficulty, keys?)yestrue when every stage was hit.
menu.lockpick(options?)aiLib.lockpick(options?)yes'opened', 'broken' or 'cancelled'.

The request shapes are MenuRequest, InputRequest, ItemPickerRequest and SkillDifficulty. Every option and listener is documented with the TypeScript twins on Shared UI.

client.lua
RegisterCommand('fine', function()
    local values = aiLib.input({
        title = 'Write a fine',
        confirm = 'Send',
        fields = {
            { name = 'amount', label = 'Amount', type = 'number', min = 1, max = 10000 },
            { name = 'reason', label = 'Reason', type = 'text', maxLength = 80 },
        },
    })
    if values then aiLib.callAction('mypolice:fine', { values.amount, values.reason }, { fallback = 'FINE_FAILED' }) end
end, false)

On this page