Understanding Entities in FiveM
In FiveM, an entity is any object that exists in the game world: vehicles, peds, props, pickups, and even the player character. Every entity has a handle (an integer ID) that you use to interact with it through native functions. Entity management is one of the most fundamental skills in FiveM development because almost every script interacts with entities in some way. Poor entity management leads to memory leaks, ghost objects that persist after a resource restarts, desync between players, and ultimately server instability. This guide covers the correct patterns for creating, tracking, and cleaning up entities.
Spawning Objects and Props
The primary native for spawning props is CreateObject (or CreateObjectNoOffset). Before spawning any object, you must request the model and wait for it to load. Failing to request the model first will result in an invisible or missing entity. Always release the model after creating the object to free memory.
-- client/entity_spawner.lua
local function SpawnProp(modelName, coords, rotation, isNetwork)
local model = joaat(modelName)
-- Request the model and wait for it to load
RequestModel(model)
local timeout = 0
while not HasModelLoaded(model) do
Wait(10)
timeout = timeout + 10
if timeout > 5000 then
print(('[ERROR] Model %s failed to load after 5s'):format(modelName))
return nil
end
end
local obj = CreateObject(model, coords.x, coords.y, coords.z, isNetwork, true, false)
if rotation then
SetEntityRotation(obj, rotation.x, rotation.y, rotation.z, 2, true)
end
-- Freeze the object so it does not fall through the ground
FreezeEntityPosition(obj, true)
-- Release the model from memory
SetModelAsNoLongerNeeded(model)
return obj
end
-- Usage
local chair = SpawnProp('prop_chair_01a', vector3(200.0, -800.0, 31.0), nil, false)
Spawning Vehicles
Vehicle spawning follows a similar pattern but uses CreateVehicle and has additional considerations for network ownership, fuel levels, and plate text. Always spawn vehicles on the server side when possible, or use server-side callbacks to validate vehicle creation requests from clients.
-- client/vehicle_spawner.lua
local function SpawnVehicle(modelName, coords, heading)
local model = joaat(modelName)
RequestModel(model)
while not HasModelLoaded(model) do
Wait(10)
end
local vehicle = CreateVehicle(model, coords.x, coords.y, coords.z, heading, true, false)
-- Basic vehicle setup
SetVehicleOnGroundProperly(vehicle)
SetEntityAsMissionEntity(vehicle, true, true)
SetVehicleHasBeenOwnedByPlayer(vehicle, true)
SetVehicleNeedsToBeHotwired(vehicle, false)
SetVehRadioStation(vehicle, 'OFF')
-- Set fuel if using a fuel system
Entity(vehicle).state:set('fuel', 100.0, true)
SetModelAsNoLongerNeeded(model)
return vehicle
end
Entity Tracking and Cleanup
The most critical aspect of entity management is tracking every entity your script creates and cleaning them up when the resource stops. Without proper cleanup, entities persist in the game world as orphans that waste memory and render resources. Use a tracking table to store all entity handles and register an onResourceStop handler that removes them.
-- client/entity_manager.lua
local ManagedEntities = {}
function RegisterEntity(entity, category)
if not DoesEntityExist(entity) then return end
ManagedEntities[entity] = {
category = category or 'default',
created = GetGameTimer(),
model = GetEntityModel(entity),
}
end
function UnregisterEntity(entity)
if ManagedEntities[entity] then
if DoesEntityExist(entity) then
SetEntityAsMissionEntity(entity, false, true)
DeleteEntity(entity)
end
ManagedEntities[entity] = nil
end
end
function CleanupCategory(category)
for entity, data in pairs(ManagedEntities) do
if data.category == category then
UnregisterEntity(entity)
end
end
end
function CleanupAllEntities()
for entity, _ in pairs(ManagedEntities) do
UnregisterEntity(entity)
end
ManagedEntities = {}
end
-- Critical: Clean up when resource stops
AddEventHandler('onResourceStop', function(resourceName)
if GetCurrentResourceName() ~= resourceName then return end
CleanupAllEntities()
end)
Network Entity Ownership
In FiveM's networked environment, every entity has an owner, the client responsible for simulating its physics and position. Network ownership determines which player's client controls the entity's movement and collision. Understanding and managing network ownership is essential for synchronized entity behavior, especially for scripts that need to control NPCs or vehicles remotely.
-- server/ownership.lua
-- Request control of a networked entity
local function RequestEntityOwnership(src, netId)
local entity = NetworkGetEntityFromNetworkId(netId)
if not DoesEntityExist(entity) then return false end
-- Set the requesting player as the owner
SetEntityRoutingBucket(entity, GetPlayerRoutingBucket(src))
return true
end
-- client/ownership.lua
-- Request network control of an entity
local function TakeEntityControl(entity, timeout)
timeout = timeout or 2000
local start = GetGameTimer()
NetworkRequestControlOfEntity(entity)
while not NetworkHasControlOfEntity(entity) do
Wait(100)
NetworkRequestControlOfEntity(entity)
if GetGameTimer() - start > timeout then
return false
end
end
return true
end
-- Usage: Move an entity you need control of
local function MoveEntity(entity, targetCoords)
if TakeEntityControl(entity) then
SetEntityCoords(entity, targetCoords.x, targetCoords.y, targetCoords.z, false, false, false, false)
return true
end
print('[WARN] Could not get control of entity')
return false
end
Spawning and Managing Peds
Ped (pedestrian) entities are used for NPCs, shop vendors, quest givers, and ambient characters. They require special handling because they have AI that can cause them to wander off, react to the environment, or attack players if not properly configured. Always freeze and disable AI for static NPCs.
-- client/ped_spawner.lua
local function SpawnStaticPed(modelName, coords, heading, scenario)
local model = joaat(modelName)
RequestModel(model)
while not HasModelLoaded(model) do
Wait(10)
end
local ped = CreatePed(0, model, coords.x, coords.y, coords.z - 1.0, heading, false, true)
-- Make the ped static and non-interactive with ambient AI
SetEntityAsMissionEntity(ped, true, true)
SetBlockingOfNonTemporaryEvents(ped, true)
SetPedFleeAttributes(ped, 0, false)
SetPedCombatAttributes(ped, 17, true)
SetPedDiesWhenInjured(ped, false)
SetEntityInvincible(ped, true)
FreezeEntityPosition(ped, true)
-- Play a scenario animation if specified
if scenario then
TaskStartScenarioInPlace(ped, scenario, 0, true)
end
SetModelAsNoLongerNeeded(model)
RegisterEntity(ped, 'npc')
return ped
end
-- Spawn a shop vendor
local vendor = SpawnStaticPed(
's_m_y_ammucity_01',
vector3(22.0, -1105.0, 29.8),
160.0,
'WORLD_HUMAN_STAND_IMPATIENT'
)
Performance Tips for Entity Management
- Limit active entities. Each entity consumes memory and render cycles. Use streaming systems to load entities when the player is nearby and unload them when they leave the area.
- Use object pools. For frequently spawned and despawned entities like bullet casings or particle effects, reuse entity handles instead of creating and deleting new ones constantly.
- Prefer client-side entities. If an entity only needs to be visible to one player (like a UI prop or preview object), create it as non-networked to reduce server load.
- Always call SetModelAsNoLongerNeeded. Loaded models stay in memory until explicitly released. Forgetting this call is one of the most common causes of memory leaks in FiveM scripts.
- Check DoesEntityExist before operations. Entities can be deleted by the game engine, other scripts, or desync. Always verify an entity still exists before trying to modify it.
- Use entity state bags for metadata. Instead of maintaining separate lookup tables for entity data, use
Entity(entity).stateto store custom properties directly on the entity.