API Reference β
Everything a resource can call β server-side mutations and reads, plus client-side helpers.
Related: Statebags (raw client data) Β· Events Β· Hooks
Before mutating: wait for
bln_society:ready. Mutating exports returnnot_readyuntil boot finishes.Money primitives:
AddMoney/RemoveMoneyhave no permission check (revenue and costs).Deposit/Withdrawcheck permissions and move player money.
Response shape β
Every mutating function answers with the same table:
{
success = true,
code = 'member_hired',
message = 'Arthur Morgan has been hired as Deputy.',
args = { 'Arthur Morgan', 'Deputy' },
data = { ... },
}code is stable across languages β branch on it and show message. Pure reads (GetBalance, HasPermission, IsOnDuty, β¦) return their value directly.
The actor argument β
Most functions take a trailing actor:
| You pass | Treated as |
|---|---|
nothing, or nil | System call β permission checks skipped, audit still written |
player source (number) | That player β permissions enforced |
| character id (string) | Same, resolved from character |
{ source = ..., charId = ... } | When you have both |
{ system = true, charId = ... } | System call attributed to a character in the audit |
Pass source when a player clicked something. Omit it when your resource acts on its own (e.g. booking a sale into the till).
Server exports β
Meta β
| Export | Arguments | Returns |
|---|---|---|
IsReady() | β | boolean |
GetConstants() | β | Const table: statuses, ledger types, actions, hook names |
GetPermissionRegistry() | β | permission groups from config |
GetPresets() | β | society presets from config |
GetSettings() | β | merged config settings |
Session β
| Export | Arguments | Returns |
|---|---|---|
GetCharacterId(source) | player source | string|nil |
GetSource(characterId) | character id | integer|nil |
GetCharacterName(characterId) | character id | string |
Societies β
| Export | Arguments | Returns |
|---|---|---|
CreateSociety(data, actor?) | see below | response, data = society |
EnsureSociety(data) | requires ownerResource + externalKey | response, creates only if missing |
GetSociety(societyId) | response | |
GetSocietyByKey(ownerResource, externalKey) | response | |
GetSocieties(filter?) | { status?, fundingSource? } | response, data = array |
UpdateSociety(societyId, data, actor?) | { name?, status?, fundingSource?, metadata?, chargeFees? } | response |
UpdateSettings(societyId, settings, actor?) | merged into settings bag | response |
GetSetting(societyId, key, fallback?) | value | |
SetFundingSource(societyId, 'account'|'state', actor?) | system only | response |
IsStateFunded(societyId) | boolean | |
SetSocietyPoint(societyId, point, actor?) | see below | response |
ClearSocietyPoint(societyId, actor?) | response | |
GetSocietyPoint(societyId) | response, data = point or nil | |
OpenSociety(societyId, source) | opens menu for player | response |
TransferOwnership(societyId, characterId, actor?) | owner or system | response |
IsOwner(societyId, characterId) | boolean | |
GetOwner(societyId) | response, data = { characterId, name } | |
DeleteSociety(societyId, actor?) | soft delete | response |
PurgeSociety(societyId) | permanent, system only | response |
CreateSociety data:
{
name = 'Valentine Sheriff',
preset = 'law',
fundingSource = 'state',
owner = characterId,
ownerName = 'Arthur Morgan',
status = 'active',
point = { ... },
ownerResource = 'bln_stores',
externalKey = 'valentine_store',
ranks = { ... },
balances = { [0] = 500.0 },
settings = { require_no_other_society = true },
metadata = { ... },
createStorages = true,
}Point shape (fields optional except coords; defaults from Config.points.defaults):
{
enabled = true,
coords = { x = -324.1, y = 776.5, z = 117.8, h = 90.0 },
blip = { enabled = true, sprite = 'blip_shop_store', name = 'Valentine Store' },
marker = { type = 'default', color = { r = 255, g = 255, b = 255, a = 180 }, scale = 1.0, distance = 15.0 },
prompt = { enabled = true, name = 'Open', key = Keys.E, distance = 2.0 },
}Society settings (UpdateSettings):
| Key | Type | Purpose |
|---|---|---|
require_no_other_society | boolean | Block hiring anyone already in another society |
inactive_days | integer | Days idle before auto fire/suspend; 0 disables |
inactive_warn_days | integer | Warn window before threshold |
inactive_action | 'fire' | 'suspend' | What the inactivity sweep does |
discord_webhook | string | Discord webhook URL for audit lines |
Ranks β
| Export | Arguments | Returns |
|---|---|---|
GetRanks(societyId) | response, ranks sorted by level | |
GetRank(societyId, rankId) | response | |
CreateRank(societyId, data, actor?) | { name, level, salary?, salaryCurrency?, permissions? } | response |
UpdateRank(societyId, rankId, data, actor?) | partial update | response |
DeleteRank(societyId, rankId, actor?) | fails while members hold it | response |
SetRankSalary(societyId, rankId, amount, actor?) | response |
Members β
| Export | Arguments | Returns |
|---|---|---|
AddMember(societyId, characterId, rankId?, actor?) | rank defaults to lowest | response |
RemoveMember(societyId, characterId, actor?) | response | |
SetMemberRank(societyId, characterId, rankId, actor?) | response | |
SetMemberStatus(societyId, characterId, 'active'|'suspended', actor?) | response | |
GetMember(societyId, characterId) | response, includes permissions | |
GetMembers(societyId) | response, array with rank and duty flags | |
IsMember(societyId, characterId) | boolean | |
GetMemberships(characterId) | response, all societies for character | |
GetPlayerMemberships(source) | online player | statebag payload |
CanHire(societyId, characterId) | dry run | response |
CountMembers(societyId) | integer |
Permissions β
| Export | Arguments | Returns |
|---|---|---|
HasPermission(societyId, characterId, permission) | boolean | |
GetPermissions(societyId, characterId) | map, wildcards expanded | |
GetPermissionOverrides(societyId, characterId) | member overrides only | |
GrantPermission(societyId, characterId, permission, actor?) | response | |
RevokePermission(societyId, characterId, permission, actor?) | explicit deny | response |
ClearPermission(societyId, characterId, permission, actor?) | inherit from rank | response |
GetRankLevel(societyId, characterId) | integer |
Duty β
| Export | Arguments | Returns |
|---|---|---|
SetDuty(societyId, characterId, onDuty, actor?) | response | |
ToggleDuty(societyId, characterId, actor?) | response | |
IsOnDuty(characterId, societyId?) | omit society = anywhere | boolean |
GetDuty(characterId) | { societyId, rankId, since }|nil | |
GetPlayerDuty(source) | statebag payload | |
GetOnDutyMembers(societyId) | array of character ids | |
CountOnDuty(societyId) | integer | |
ForceOffDuty(societyId, characterId, reason?) | system | response |
GetActivity(societyId) | response, per-member activity | |
GetLastSeen(societyId) | response, last seen timestamps | |
GetInactivityReport(societyId) | read only | response |
SweepInactive(societyId?) | run sweep now | response |
Finance β
Currencies: 0 = cash, 1 = gold, 2 = rol (Const.CASH, Const.GOLD, Const.ROL).
| Export | Arguments | Returns |
|---|---|---|
GetBalance(societyId, currency?) | number | |
GetBalances(societyId) | { [currency] = number } | |
CanAfford(societyId, amount, currency?) | boolean | |
AddMoney(societyId, amount, opts?) | no permission check | response |
RemoveMoney(societyId, amount, opts?) | no permission check | response |
Deposit(societyId, amount, opts?, actor?) | from player pocket | response |
Withdraw(societyId, amount, opts?, actor?) | to player pocket | response |
Transfer(fromId, toId, amount, opts?, actor?) | response | |
GetLedger(societyId, filters?, actor?) | paged | response |
GetLedgerSummary(societyId, currency?, days?) | { income, expenses, net, days } | |
GetStateSpending(societyId?, days?) | state-funded wages | number |
Finance opts:
{
currency = 0,
type = 'sale',
reason = 'Cart sold',
ref = 'store:sale:8821', -- idempotency key
actorId = characterId,
targetId = characterId,
metadata = { ... },
}Pass a unique ref to make retries safe β duplicate ref returns the original entry.
Payroll and paychecks β
| Export | Arguments | Returns |
|---|---|---|
RunPayroll(societyId, actor?) | response with totals | |
GetPayrollHistory(societyId, filters?, actor?) | paged | response |
GetSalary(societyId, characterId) | number | |
GetPayrollEligible(societyId) | array | |
GetPaychecks(societyId, characterId) | response | |
GetPendingPay(characterId) | all societies | map |
CollectPaychecks(societyId, actor) | response | |
CollectAllPaychecks(actor) | response | |
IssuePaycheck(societyId, characterId, amount, opts?) | bonus, etc. | response |
Storage β
| Export | Arguments | Returns |
|---|---|---|
GetStorages(societyId, charId?) | response | |
GetStorage(societyId, key) | response | |
OpenStorage(societyId, key, actor) | opens inventory | response |
BuyStorage(societyId, actor, payload) | paid from account | response |
SetStorageAccess(societyId, key, minRankLevel, actor?) | response | |
CalculateStorageCost(slots, maxWeight, acceptWeapons) | response | |
CreateStorage(societyId, key, opts?) | response |
Fines β
| Export | Arguments | Returns |
|---|---|---|
IssueFine(societyId, characterId, amount, reason, actor?) | response | |
PayFine(fineId, actor) | response | |
CancelFine(fineId, actor?) | response | |
GetFines(societyId, filters?, actor?) | paged | response |
GetCharacterFines(characterId) | response |
Action fees β
| Export | Arguments | Returns |
|---|---|---|
GetFees(societyId, actor?) | response | |
CalculateFee(societyId, action, baseAmount?) | preview | response |
ChargeFee(societyId, action, baseAmount?, opts?) | manual charge | response |
Fee actions: payroll_run, member_hire, finance_withdraw, finance_transfer, society_rename, storage_buy.
Logs β
| Export | Arguments | Returns |
|---|---|---|
Log(societyId, action, opts?) | custom audit line | response |
GetLogs(societyId, filters?, actor?) | paged | response |
Directory and hooks registry β
| Export | Arguments | Returns |
|---|---|---|
GetDirectory() | public society directory | |
GetRevision() | integer, bumped on structural change | |
RegisterHook(name, handler) | hook id | |
RemoveHook(name, id) | β | |
ListHooks() | hook names |
Society handle β
For code that works with one society at a time:
local society = exports.bln_society:Society(societyId)
local society = exports.bln_society:SocietyByKey('bln_stores', 'valentine_store')
local society = exports.bln_society:EnsureSocietyHandle({ ... })Methods (same as exports, without societyId):
Exists Get Data Update Delete Settings Setting UpdateSettingsIsStateFunded SetFunding Owner IsOwner TransferOwnershipPoint SetPoint ClearPoint OpenRanks Rank CreateRank UpdateRank DeleteRank SetRankSalaryMembers Member IsMember AddMember RemoveMember SetMemberRank SetMemberStatus CanHireHasPermission Permissions GrantPermission RevokePermission LevelSetDuty ToggleDuty IsOnDuty OnDuty CountOnDutyBalance Balances CanAfford AddMoney RemoveMoney Deposit Withdraw TransferTo Ledger LedgerSummaryRunPayroll PayrollHistory Salary Paychecks CollectPaychecks IssuePaycheckStorages OpenStorage BuyStorage SetStorageAccess CreateStorageFines IssueFineFees CalculateFee ChargeFeeLogs Log
if society:HasPermission(charId, 'finances.withdraw') then
society:Withdraw(100, { reason = 'Restock' }, source)
endClient exports β
Convenience wrappers around statebags β permission checks, wildcards, and filtering handled for you. No server round trip.
| Export | Returns |
|---|---|
IsReady() | boolean |
GetMemberships() | every society you belong to, keyed by id as string |
GetMembership(societyId) | one entry, or nil |
IsMember(societyId) | boolean |
HasPermission(societyId, permission) | boolean, wildcards resolved |
IsOnDuty(societyId?) | boolean |
GetDuty() | { id, name, type, rankId, since } or false |
GetActiveSociety() | membership you are on duty for |
GetPendingPay(societyId?) | { count, amounts } |
GetBalance(societyId, currency?) | number|nil, requires finances.view |
GetStorage(societyId, key?) | tier info, requires storage.use |
GetDirectory() | every society: name, member count, on duty count |
GetRevision() | integer β compare to invalidate cache |
GetPlayerMemberships(serverId) | another player's memberships |
GetPlayerDuty(serverId) | another player's duty |
OpenUi(societyId) / OpenSociety(societyId) / CloseUi() | management menu |
IsUiOpen() | boolean |
ToggleDuty(societyId?) | asks server |
OpenStorage(societyId, key) | asks server |
GetStorageInfo(societyId, key) | tier info from bag |
Error codes β
Branch on response.code. Every code has a line in config/locale.cfg.lua.
General β generic_error, db_error, rate_limited, invalid_request, invalid_amount, player_offline, character_not_found, feature_disabled, blocked_by_hook, not_ready
Society β society_not_found, not_member, no_permission, admin_only, point_invalid
Members β already_member, target_not_member, cannot_target_self, target_outranks_you, cannot_remove_owner
Hiring β target_in_other_society, too_many_societies
Ranks β rank_not_found, rank_in_use, rank_level_taken, rank_above_you, cannot_delete_owner_rank, invalid_rank_name
Duty β already_on_duty, not_on_duty, duty_elsewhere
Finance β insufficient_funds, insufficient_player_funds, transfers_disabled, negative_balance_blocked
Payroll β payroll_no_members, payroll_unaffordable, no_paychecks, paycheck_too_far
Storage β storage_not_found, storage_max_tier, storage_no_access
Fines β fine_not_found, fine_not_pending
Success codes β society_created, member_hired, member_promoted, deposited, withdrawn, payroll_done, paycheck_collected, storage_upgraded, fine_issued, point_updated, and more.
