State
How Shiva manages server-side and client-side state.
Server State
Server state lives in services. Services are singletons (by default) managed by the container. State is loaded from the database on startup and cached in memory.
lua
-- PlayerService holds player state in memory
local PlayerService = {}
local _players = {} -- { [serverId] = PlayerData }
function PlayerService:get(serverId)
return _players[serverId]
end
function PlayerService:load(serverId)
local data = DB:first('SELECT * FROM players WHERE server_id = ?', serverId)
_players[serverId] = data
endPersisting State
Use the Database API for persistence. Shiva provides a thin wrapper around oxmysql:
lua
local DB = require('shiva-fw.database')
-- Async query
DB:execute('UPDATE accounts SET amount = ? WHERE player_id = ?', { amount, playerId })
-- Sync scalar
local balance = DB:scalar('SELECT amount FROM accounts WHERE player_id = ?', playerId)Client State
Client state is managed through FiveM's StateBag system and mirrored from the server:
lua
-- Server: set state
local ped = GetPlayerPed(source)
Entity(ped).state:set('health', health, true)
-- Client: read state
local health = LocalPlayer.state.healthShared State
For state that needs to be shared between client and server but doesn't need to be in the database, use the Cache API:
lua
local Cache = require('shiva-fw.cache')
local cache = Cache.new({ ttl = 60 }) -- 60 second TTL
cache:set('player:' .. playerId .. ':coords', coords)
local coords = cache:get('player:' .. playerId .. ':coords')State Lifecycle
| Event | When | What to do |
|---|---|---|
player:connected | Player joins | Load player data into memory |
player:spawned | Character selected | Load character data |
player:disconnected | Player leaves | Flush state to DB, clean up memory |