Developer API
Other resources can open the selector, add their own locations, lock locations with their own rules and react when a player spawns.
Two ways to call it
Exports work from anywhere:
exports['koja-spawnselector']:Open({ canCancel = true })
import.lua gives you a SpawnSelector table with every export as a function, plus OpenAsync and SpawnAtAsync on the client:
shared_script '@koja-spawnselector/import.lua'SpawnSelector.Open({ canCancel = true })
local spawn = SpawnSelector.OpenAsync({ canCancel = true })
Client
Open
local opened = exports['koja-spawnselector']:Open(options)
Returns false when the selector is already open, busy, or ClientEditable.CanOpen refused. Every option is optional:
| Option | Type | What it does |
|---|---|---|
isNew | boolean | New character: list Config.NewCharacter.Spawns instead of the normal list |
canCancel | boolean | Allow ESC and the exit button. Default Config.Open.CanCancel |
categories | string[] | Only these category ids |
lastLocation | boolean | false hides "Last location" |
spawns | spawn[] | Extra locations for this opening only (client side, see below) |
includeServer | boolean | false lists only spawns and the client registered ones |
selected | string | Preselected spawn id |
cinematic | boolean | Camera flight on or off for this opening |
title | string | Replaces the panel title |
onSpawn | function(spawn) | Called after the player landed |
onCancel | function() | Called whenever the selector closes without a spawn (ESC, Close, server Close) |
exports['koja-spawnselector']:Open({
isNew = true,
onSpawn = function(spawn)
print(('spawned at %s'):format(spawn.label))
end,
})OpenAsync (import.lua)
CreateThread(function()
local spawn = SpawnSelector.OpenAsync({ canCancel = true, categories = { 'property' } })
if not spawn then return end -- cancelled
print(spawn.id, spawn.coords.x, spawn.coords.y)
end)
Takes the same options as Open. Returns the spawn, or nil when it was cancelled or could not open.
Close, IsOpen
exports['koja-spawnselector']:Close() -- calls onCancel
exports['koja-spawnselector']:IsOpen() -- true while open or while the camera flies
RegisterSpawn, RemoveSpawn
A location only this client sees, until you remove it or the resource restarts. Registering the same id again replaces it.
exports['koja-spawnselector']:RegisterSpawn({
id = 'motel_room',
label = 'Pink Cage Motel',
description = 'Room 4',
category = 'property',
coords = vec4(312.3, -218.6, 54.2, 160.0),
})
exports['koja-spawnselector']:RemoveSpawn('motel_room')
SpawnAt, Fly, SpawnAtAsync
The same camera flight without the selector — after a jail release, a respawn or your own creator.
exports['koja-spawnselector']:SpawnAt(vec4(1846.4, 2585.9, 45.7, 270.0), {
label = 'Bolingbroke',
description = 'Released',
cinematic = true,
overlay = true,
onLanded = function(coords) print('landed') end,
})
exports['koja-spawnselector']:Fly(vec4(-160.0, 6420.0, 31.9, 135.0), { label = 'Paleto Bay' })
| Option | What it does |
|---|---|
label, description | Text of the letterbox caption. Default: the street name |
cinematic | Camera flight (true) or a plain fade. Default Config.Cinematic.Enabled |
overlay | false hides the caption |
onLanded | function(coords), called after the player landed |
SpawnAt returns true right away (false for invalid coords or while another spawn runs). Fly is SpawnAt with cinematic = true.
With import.lua, SpawnSelector.SpawnAtAsync(coords, options) waits until the player landed.
Server
Open, Close, IsSelecting
exports['koja-spawnselector']:Open(source, { canCancel = true })
exports['koja-spawnselector']:Close(source)
exports['koja-spawnselector']:IsSelecting(source)
The options are the client ones without functions (they cannot cross the network). Listen to the events below instead.
RegisterSpawn, RegisterSpawns, RemoveSpawn
Locations for every player, with the same locks as Config.Spawns. They are removed automatically when your resource stops.
exports['koja-spawnselector']:RegisterSpawns({
{ id = 'lsfd', label = 'Davis Fire Station', category = 'job', coords = vec4(204.8, -1641.6, 29.8, 320.0), jobs = { fire = 0 }, onDuty = true },
{ id = 'vu', label = 'Vanilla Unicorn', category = 'job', coords = vec4(129.2, -1299.3, 29.2, 210.0), jobs = 'unicorn' },
})
RegisterSpawn returns true when the definition is valid, RegisterSpawns returns how many were added.
RegisterProvider, RemoveProvider
A provider returns the locations of one player and is called every time that player opens the selector. Use it for houses, apartments, garages, motels — anything a player owns.
exports['koja-spawnselector']:RegisterProvider('my-housing', function(source, identifier)
local houses = MySQL.query.await('SELECT id, label, x, y, z, h FROM houses WHERE owner = ?', { identifier })
local list = {}
for _, house in ipairs(houses or {}) do
list[#list + 1] = {
id = 'house:' .. house.id,
label = house.label,
category = 'property',
coords = vec4(house.x, house.y, house.z, house.h),
data = { house = house.id },
}
end
return list
end)
identifier is the character identifier from koja-lib (ESX identifier, QBCore citizenid). Errors are caught and printed, so one broken provider does not break the screen. Providers are removed when your resource stops.
AddAccessCheck, RemoveAccessCheck
Your own lock rule, run for every listed location after the config locks:
exports['koja-spawnselector']:AddAccessCheck('jail', function(source, spawn)
if Player(source).state.jailed and spawn.id ~= 'prison' then
return false, 'In prison'
end
return true
end)
Return false, reason to lock the spot; the reason is shown as its label. Anything else allows it. Checks are removed when your resource stops.
RegisterCategory
exports['koja-spawnselector']:RegisterCategory({ id = 'business', label = 'Business', icon = 'building', order = 35 })
order is compared with the order of Config.Categories.
GetSpawns
local list = exports['koja-spawnselector']:GetSpawns(source, { isNew = false })
The exact list this player would see, with locked and lockedLabel filled in.
GetLastLocation, SetLastLocation
local last = exports['koja-spawnselector']:GetLastLocation(source) -- or a character identifier
-- { coords = { x, y, z, w }, updated = 1793532461 } or nil
exports['koja-spawnselector']:SetLastLocation(source, vec4(1846.4, 2585.9, 45.7, 270.0))
Set it before a player logs out somewhere you do not want them back (a heist vault, an event island).
The spawn object
onSpawn, OpenAsync and the events give you:
| Field | Description |
|---|---|
id, label, description, category, icon, image | From the definition |
coords | { x, y, z, w } |
data | Your data table |
origin | config, server, provider, last or client |
updated | Last location only: unix time it was saved |
street, zone | Client only: street and zone name at the spot |
Events
| Event | Side | Arguments | When |
|---|---|---|---|
koja-spawnselector:client:opened | Client | options | The map is shown |
koja-spawnselector:client:spawned | Client | spawn, isNew | The player landed |
koja-spawnselector:client:closed | Client | options | Closed without a spawn |
koja-spawnselector:server:spawned | Server | source, spawn, isNew | The player landed |
AddEventHandler('koja-spawnselector:server:spawned', function(source, spawn, isNew)
if spawn.origin == 'provider' and spawn.data and spawn.data.house then
TriggerEvent('my-housing:server:enter', source, spawn.data.house)
end
end)On the server, spawn is the location the server listed and checked itself — not something the client sent — so its data is safe to act on.
The only exception is origin == 'client': a location that came from client side RegisterSpawn / spawns, which the server never saw. Do not trust its data for ownership.
You can also open and close from the server with TriggerClientEvent('koja-spawnselector:client:open', source, options) and TriggerClientEvent('koja-spawnselector:client:close', source).