Spawnmanager exports and respawn behavior
The stock spawnmanager controls the local player’s spawning. These are client exports, not server-side character or inventory APIs. A resource using them should declare dependency 'spawnmanager' and have one clearly identified owner of the spawn flow.
Do not add this lab alongside a framework’s character selector, hospital or spawn controller on a live server. Test on an isolated server first. This guide documents the provider implementation at c6afa39 , not every replacement resource with the same export names.
All seven client exports
Call on exports.spawnmanager | Input and result | Important behavior |
|---|---|---|
addSpawnPoint(spawn) | A spawn table; returns its assigned ID | Validates coordinates, heading and ped model; changes the table’s model to a hash and adds idx. |
removeSpawnPoint(id) | The ID returned by addSpawnPoint | Removes that registration. Keep the ID rather than guessing it. |
loadSpawns(jsonString) | JSON text containing a spawns array | Adds those registrations. It does not read a filename, replace the existing list or return their IDs. |
spawnPlayer(spawn, callback) | A spawn table, current list position, or omitted selection | Starts asynchronous spawning. The optional callback receives the spawn table, not a new player ID. |
setAutoSpawn(enabled) | Boolean | Controls the automatic-spawn monitor. |
setAutoSpawnCallback(callback) | Callback, or nil to clear it | Replaces the monitor’s default action and also enables autospawn. Your callback must perform the spawn. |
forceRespawn() | No arguments | Requests a monitor-driven respawn; the player must be active and autospawn enabled. |
spawnPlayer is not a boolean success function. In the reviewed implementation an existing spawn lock can cause an early return without calling the callback. Model loading can also delay completion. Do not build a retry loop that repeatedly forces respawns or assumes a callback always arrives immediately.
Registered IDs are not always list positions
addSpawnPoint allocates an increasing idx, and removeSpawnPoint searches for that value. However, the numeric overload of spawnPlayer selects an array position. Removing an earlier registration can make these two numbers differ.
For example, add A and B, remove A, and B can retain registration ID 2 while occupying list position 1. Passing B’s registration ID to spawnPlayer is then unsafe. Use the returned ID for removal and a fresh, explicit spawn table for deterministic spawning. Do not rely on an invalid selection being rejected cleanly: the reviewed provider accesses spawn data before its later invalid-index check.
A minimal manual-spawn resource
This lab deliberately does not enable automatic spawning or replace another resource’s callback. It still changes the local player’s model and spawn state, so use it only in a disposable test session with other spawn owners stopped.
Create resources/[local]/doc_spawn/fxmanifest.lua:
fx_version 'cerulean'
game 'gta5'
dependency 'spawnmanager'
client_script 'client.lua'Create client.lua:
local registeredId
local pending = false
local model = joaat('a_m_y_business_01')
local function spawnDefinition()
return {
x = -1037.7, y = -2737.8, z = 20.2,
heading = 330.0, model = model
}
end
local function registerPoint()
if registeredId then return end
if not IsModelInCdimage(model) or not IsModelAPed(model) then
print('doc_spawn: the configured ped model is unavailable')
return
end
registeredId = exports.spawnmanager:addSpawnPoint(spawnDefinition())
end
AddEventHandler('onClientResourceStart', function(name)
if name == GetCurrentResourceName() or name == 'spawnmanager' then
registerPoint()
end
end)
RegisterCommand('docspawn', function()
if pending or not registeredId then return end
if GetResourceState('spawnmanager') ~= 'started' then return end
if not NetworkIsPlayerActive(PlayerId()) then return end
pending = true
exports.spawnmanager:spawnPlayer(spawnDefinition(), function(spawn)
pending = false
print(('Spawn completed at %.1f, %.1f, %.1f'):format(spawn.x, spawn.y, spawn.z))
end)
end, false)
AddEventHandler('onClientResourceStop', function(name)
if name == 'spawnmanager' then
registeredId = nil
pending = false
return
end
if name ~= GetCurrentResourceName() then return end
if registeredId and GetResourceState('spawnmanager') == 'started' then
exports.spawnmanager:removeSpawnPoint(registeredId)
end
registeredId = nil
end)In the server console run refresh, ensure spawnmanager and ensure doc_spawn. Once connected and active, run docspawn in F8. The expected callback reports the configured coordinates. The provider also emits the local playerSpawned event before calling this callback.
The pending flag prevents this lab from starting overlapping requests; it is not a timeout or an override of another resource’s lock. If no callback arrives, inspect F8 for model/loading errors and competing spawn owners. Do not clear the flag in a repeating timer to conceal a stuck spawn.
The stock implementation clears weapons and wanted state during spawning. This is another reason not to use the lab as a production character selector. A client playerSpawned event is not trustworthy evidence for granting server-side items or money.
Load JSON, not a path
This separate example shows the accepted data shape; it is not required by the lab:
local definitions = {
spawns = {
{ x = -1037.7, y = -2737.8, z = 20.2,
heading = 330.0, model = 'a_m_y_business_01' }
}
}
exports.spawnmanager:loadSpawns(json.encode(definitions))Do not pass 'spawns.json' or the Lua table directly. Reading a file is a separate operation. The JSON needs the outer spawns field, and each registration needs valid coordinates, heading and a ped model. When you need to remove exactly the entries you added, call addSpawnPoint individually and retain each returned ID instead.
Mapmanager’s spawnpoint directive is a separate integration; it is not what loadSpawns means.
Automatic-spawn ownership and cleanup
Calling setAutoSpawnCallback also enables the monitor. A callback that only logs a message replaces the default spawn without actually spawning anyone. An empty spawn list is not a valid random-spawn configuration, and forceRespawn() alone does not enable the monitor.
For a dedicated resource that owns autospawn, cleanup order matters:
-- Only the resource that owns the automatic-spawn policy should do this.
exports.spawnmanager:setAutoSpawnCallback(nil)
exports.spawnmanager:setAutoSpawn(false)Clearing the callback first avoids unintentionally re-enabling autospawn after disabling it. The manual lab above never acquires this ownership and therefore does not alter either setting during cleanup.
Acceptance checklist and scope
Test one manual spawn, a repeated command during spawning, a doc_spawn restart and a spawnmanager restart. Confirm only the lab’s registration is removed on stop and the point is registered again after the provider restarts. Stop the lab, remove its startup entry and reconnect to restore your normal character flow; stopping a resource does not undo an already completed model change.
This is a source-reviewed example, not an in-game certified spawn resource. Native dispatch, collision at the chosen coordinates and track-specific behavior still require a recorded client/server run. See the stock-resource overview, playerSpawned event and official spawnmanager manual for related contracts.