Understanding State Bags in FiveM/RedM: A Complete Guide

Published on
5 mins read
--- views

Understanding State Bags in FiveM/RedM: A Complete Guide

Introduction

State Bags are a powerful feature in FiveM/RedM that enable efficient data synchronization across multiple players and entities. Introduced in version v2843 as a OneSync feature, State Bags provide a more robust alternative to traditional Decors, offering server-side access and modification capabilities.

Prerequisites

Before diving into State Bags, ensure you have:

  • OneSync enabled (State Bags won't work without it)
  • Basic understanding of FiveM/RedM scripting
  • Familiarity with Lua programming

Types of State Bags

There are three main types of State Bags in FiveM/RedM:

  1. Entity State Bags
    • Attached to specific entities
    • Controlled by entity owner and server
  2. Player State Bags
    • Specific to players
    • Can be written by the player and server
  3. Global State Bags
    • Server-wide state
    • Server-set, client-read
    • Shared across all resources

Basic Usage

Entity State Bags

-- Server-side
local vehicle = GetVehiclePedIsIn(ped, false)
local state = Entity(vehicle).state

-- Setting state
state.damage = 100

-- Getting state
local currentDamage = state.damage

-- Setting with replication control
Entity(vehicle).state:set('damage', 100, true) -- replicate to clients

Player State Bags

-- Server-side
local player = Player(source).state
player.health = 100

-- Client-side
LocalPlayer.state.health = 100

-- Accessing other player's state
local playerHealth = Player(id).state.health

-- Replicating from client
LocalPlayer.state:set('health', 75, true) -- replicate to server

Global State Bags

-- Server-side setting
GlobalState.weatherType = 'RAIN'

-- Client-side reading
local currentWeather = GlobalState.weatherType

-- Note: Clients cannot modify GlobalState

Replication Rules

Entity State Replication

  1. Server-Side

    -- Automatically replicates to all clients
    Entity(vehicle).state.fuel = 100
    
    -- Optional non-replication
    Entity(vehicle).state:set('localData', 'value', false)
    
  2. Client-Side

    -- Local only by default
    Entity(vehicle).state.customData = 'value'
    
    -- Explicit replication
    Entity(vehicle).state:set('customData', 'value', true)
    

Player State Replication

-- Server controls replication
Player(source).state.isAlive = true -- replicates

-- Client needs explicit replication
LocalPlayer.state:set('isReady', true, true) -- replicates to server

State Bag Change Handlers

State Bag change handlers allow you to monitor and react to state changes in real-time.

-- Basic change handler
AddStateBagChangeHandler('health', nil, function(bagName, key, value, _unused, replicated)
    print(string.format("Health changed to: %s", value))
end)

-- Entity-specific handler
AddStateBagChangeHandler('fuel', 'entity:5', function(bagName, key, value, _unused, replicated)
    if value < 10 then
        print("Low fuel warning!")
    end
end)

Advanced Handler Example

-- Explosion handler based on state
AddStateBagChangeHandler('exploded', nil, function(bagName, key, value, _unused, replicated)
    -- Only process when value is true
    if not value then return end
    
    -- Get entity from bag name
    local entity = GetEntityFromStateBagName(bagName)
    if entity == 0 then return end
    
    -- Get coordinates and create explosion
    local coords = GetEntityCoords(entity)
    AddExplosion(coords, 0xFFFFFFFF, 1.0, true, false, 1.0)
end)

Best Practices and Limitations

State Access Optimization

-- Inefficient
local x = Entity(vehicle).state.data.x
local y = Entity(vehicle).state.data.y

-- Better Approach
local state = Entity(vehicle).state
local x = state.data.x
local y = state.data.y

Shallow State Limitations

-- Won't work (nested modification)
Entity(vehicle).state.data.subset = 'value'

-- Correct approach
Entity(vehicle).state['data:subset'] = 'value'

Data Size Considerations

-- For large data, use latent events instead
-- Bad:
Entity(vehicle).state.hugeData = veryLargeTable

-- Better:
TriggerLatentClientEvent('syncLargeData', player, 1000, veryLargeTable)

Practical Examples

Vehicle Damage System

-- Server-side initialization
RegisterNetEvent('vehicleSpawned', function(netId)
    local vehicle = NetworkGetEntityFromNetworkId(netId)
    Entity(vehicle).state.damage = 0
    Entity(vehicle).state.engineHealth = 1000
end)

-- Client-side damage handler
AddStateBagChangeHandler('damage', nil, function(bagName, key, value)
    local vehicle = GetEntityFromStateBagName(bagName)
    if vehicle == 0 then return end
    
    -- Apply visual damage
    SetVehicleDamageModifier(vehicle, value / 100)
end)

Player Status System

-- Server-side player status
RegisterNetEvent('playerLoaded', function()
    Player(source).state.hunger = 100
    Player(source).state.thirst = 100
end)

-- Client-side status update
CreateThread(function()
    while true do
        Wait(60000) -- Every minute
        LocalPlayer.state:set('hunger', (LocalPlayer.state.hunger or 100) - 1, true)
        LocalPlayer.state:set('thirst', (LocalPlayer.state.thirst or 100) - 1.5, true)
    end
end)

Global Weather System

-- Server-side weather control
CreateThread(function()
    local weatherTypes = {'CLEAR', 'CLOUDY', 'RAIN', 'THUNDER'}
    while true do
        Wait(300000) -- Every 5 minutes
        local newWeather = weatherTypes[math.random(#weatherTypes)]
        GlobalState.currentWeather = newWeather
    end
end)

-- Client-side weather sync
AddStateBagChangeHandler('currentWeather', nil, function(_, _, value)
    SetWeatherTypeOverTime(value, 15.0)
end)

Troubleshooting Common Issues

  1. State Not Syncing

    -- Check network ownership
    if not NetworkGetEntityOwner(entity) then
        return
    end
    
  2. Undefined States

    -- Always check if state exists
    local damage = Entity(vehicle).state.damage
    if damage == nil then
        damage = 0
    end
    
  3. Replication Issues

    -- Ensure proper replication flags
    LocalPlayer.state:set('status', 'ready', true) -- Must set true for client->server
    

Conclusion

State Bags provide a powerful and efficient way to synchronize data across your FiveM/RedM server. By following these best practices and understanding the limitations, you can create robust and performant systems for your server.

Remember key points:

  • Use appropriate state bag types for your needs
  • Be mindful of data size and replication
  • Implement proper error handling
  • Consider performance implications in high-traffic scenarios

Additional Resources

Join Our Community!

Get help, share ideas, get free scripts, and connect with other RedM enthusiasts in our Discord server.

Join Discord