The util module loads first on both server and client. It holds the small helpers the rest of the library is built on: prefixed logging, string and table helpers, a polling helper, and two functions for answering exports on behalf of another resource.
Logging
Every message is prefixed with your resource name in brackets. The message is a string.format pattern, and any extra arguments are format arguments.
| Function | Prefix colour | Use |
|---|---|---|
encore.print(message, ...) | Cyan | General information |
encore.warn(message, ...) | Yellow | Something went wrong but the resource carries on |
encore.warnOnce(key, message, ...) | Yellow | Warn only the first time a given key is seen, for conditions that would otherwise repeat every tick |
encore.print('Loaded %d loot tables', encore.table.size(LootTables))
encore.warn('Unknown loot table "%s"', name)
-- inside a loop that runs every frame
encore.warnOnce('missing-model:' .. model, 'Model %s does not exist', model)Because the message is a format pattern, a stray % in it breaks the call. Never pass player-authored text as the message. Pass it as an argument instead: encore.warn('Bad name: %s', name).
Strings
| Function | Returns | Notes |
|---|---|---|
encore.string.trim(str) | string | Strips leading and trailing whitespace |
encore.string.capitalize(str) | string | First letter upper case, the rest lower case |
encore.string.split(str, delimiter) | string[] | Splits on a literal delimiter, not a Lua pattern, so . and % are safe. Empty pieces are kept |
encore.string.trim(' Scrap Dealer ') -- 'Scrap Dealer'
encore.string.capitalize('rADIO') -- 'Radio'
encore.string.split('group:12', ':') -- { 'group', '12' }
encore.string.split('a..b', '.') -- { 'a', '', 'b' }Maths
| Function | Returns | Notes |
|---|---|---|
encore.math.round(num, decimals?) | number | Rounds half up to decimals places (default 0) |
encore.math.clamp(num, low, high) | number | Keeps num between low and high |
encore.math.round(12.3456, 2) -- 12.35
encore.math.clamp(amount, 1, 100) -- never below 1 or above 100Tables
| Function | Returns | Notes |
|---|---|---|
encore.table.size(tbl) | number | Counts every key, not just the array part |
encore.table.contains(tbl, value) | boolean | Searches all values with pairs |
encore.table.mapBySubfield(tbl, field) | table | Re-keys entries by one of their fields. Entries without the field are skipped |
encore.table.clone(value) | copy | Deep copy. Handles cycles and repeated references, and keeps metatables |
encore.array.contains(arr, value) | boolean | Searches indexes 1..#arr only |
local byName = encore.table.mapBySubfield({
{ name = 'bandage', heal = 10 },
{ name = 'medkit', heal = 50 },
}, 'name')
-- byName.medkit.heal == 50
local defaults = encore.table.clone(Config.defaultLoadout) -- safe to modifyPolling with waitFor
encore.waitFor(fn, timeout?, interval?) calls fn until it returns something other than nil, then returns that value.
| Parameter | Default | Meaning |
|---|---|---|
fn | required | Called at least once. Return nil to keep waiting |
timeout | 10000 | Milliseconds before giving up. Returns nil on timeout |
interval | every frame | Milliseconds between checks |
waitFor yields, so call it from a thread, event handler or callback, never from the main chunk of a file.
false is not nil. If fn returns false, the wait ends and waitFor returns false. Return nil when you mean "not yet".
local netId = encore.waitFor(function()
local id = NetworkGetNetworkIdFromEntity(vehicle)
return id ~= 0 and id or nil
end, 5000, 100)
if not netId then return encore.warn('vehicle never networked') endExport bridging
These two functions are what EndCore's QBCore, Qbox and ESX compatibility layers are built on. You only need them if you are writing a bridge or an adapter.
encore.provideExport
encore.provideExport(resource, name, fn) answers exports[resource]:name(...) on behalf of another resource name.
In FiveM, exports('Foo', fn) is shorthand for listening on the event __cfx_export_<self>_Foo. Nothing stops a resource from listening for a name it doesn't own, so provideExport listens on __cfx_export_<resource>_<name> and hands back fn.
A bridge must not also call exports() for the same names. Two answers for one export name is a race, and wrapping a name the host already exports can make it call itself.
encore.shouldBridge
encore.shouldBridge(resource) returns true when it is safe to impersonate resource. It returns false when:
- the convar
encore:disablebridgeis'true', or - the real resource is
startedorstarting. In that case it prints<resource> is running; skipping its compatibility bridge.
-- my-bridge/server.lua
if not encore.shouldBridge('some_legacy_core') then return end
encore.provideExport('some_legacy_core', 'GetPlayerMoney', function(source)
return exports['en-core']:GetMoney(source, 'cash')
end)See Compatibility bridges for the bridges en-core ships and how to turn them off.