Parent : [[INDEX-FIVEM-BACKEND]]
Anti-Pattern : Global State Abuse (FiveM)
Erreur à éviter
Utiliser des variables globales pour stocker l'état des joueurs/systemes, créant des conflits et des bugs difficiles à tracer.
Exemple incorrect
-- ❌ DANGER - Variables globales
-- global.lua (horrible)
isDead = false -- Global modifié par tout le monde !
currentHealth = 200
playerInventory = {}
closestPlayer = nil
citizen.CreateThread(function()
while true do
-- Problème : tout le monde partage la même variable
if isDead then
-- Revival logic - mais isDead est global !
end
Wait(1000)
end
end)
-- Dans un autre script
RegisterNetEvent('playerDied')
AddEventHandler('playerDied', function()
isDead = true -- Modifie pour tout le monde !
end)
Pourquoi c'est dangereux
- Conflits : 2 joueurs modifient la même variable
- Race conditions : Un thread écrase la valeur d'un autre
- Debugging nightmare : Impossible de savoir qui a modifié quoi
- Memory leaks : Variables jamais nettoyées
Bonne pratique
-- ✅ CORRECT - Tables locales par player
-- state.lua
local PlayerStates = {}
function GetPlayerState(playerId)
if not PlayerStates[playerId] then
PlayerStates[playerId] = {
isDead = false,
health = 200,
inventory = {},
lastPosition = nil,
metadata = {}
}
end
return PlayerStates[playerId]
end
function SetPlayerState(playerId, key, value)
local state = GetPlayerState(playerId)
state[key] = value
-- Trigger event pour synchronisation si nécessaire
TriggerEvent('wasabi_state:changed', playerId, key, value)
end
-- Utilisation
RegisterNetEvent('wasabi_ambulance:playerDied')
AddEventHandler('wasabi_ambulance:playerDied', function()
local src = source
SetPlayerState(src, 'isDead', true)
SetPlayerState(src, 'deathTime', os.time())
end)
-- Cleanup quand le joueur quitte
AddEventHandler('playerDropped', function()
local src = source
PlayerStates[src] = nil -- Nettoyage mémoire
end)
Pattern : State Manager
-- wasabi_statemanager.lua
local StateManager = {}
local states = {}
function StateManager:InitPlayer(playerId)
states[playerId] = {
medical = {
isDead = false,
injuries = {},
bloodLevel = 100
},
position = {
last = nil,
current = nil
},
actions = {
lastHeal = 0,
lastRevive = 0
}
}
end
function StateManager:Get(playerId, category, key)
if not states[playerId] then return nil end
if not states[playerId][category] then return nil end
return states[playerId][category][key]
end
function StateManager:Set(playerId, category, key, value)
if not states[playerId] then
self:InitPlayer(playerId)
end
if not states[playerId][category] then
states[playerId][category] = {}
end
states[playerId][category][key] = value
end
function StateManager:Cleanup(playerId)
states[playerId] = nil
end
-- Export pour usage externe
exports('GetStateManager', function() return StateManager end)
Migration depuis global
-- ❌ AVANT (global)
isDead = true
-- ✅ APRÈS (encapsulé)
local PlayerData = exports['wasabi_core']:GetPlayerData(source)
PlayerData.medical.isDead = true
exports['wasabi_core']:SavePlayerData(source, PlayerData)
Règles d'or
- Aucune variable globale pour l'état mutable
- Tables locales indexées par player ID
- Cleanup automatique sur playerDropped
- Exports/API pour accès contrôlé
Références
- Pattern WASABI : Gestion d'état médical
- Lua best practices : Local vs Global
- ESX PlayerData pattern