State bags FiveM : Entity(entity).state, GlobalState et handlers
Comment fonctionnent les state bags FiveM : Entity(entity).state, Player(source).state, LocalPlayer.state, GlobalState, :set, change handlers, et quand les préférer aux événements
Vous voulez partager une petite donnée entre le serveur et chaque client, par exemple qu'un véhicule est verrouillé, qu'un joueur est menotté, ou la météo actuelle, et vous continuer à écrire des événements qui se déclenchent à chaque changement et se désynchronisent pour les joueurs qui rejoignent plus tard. Les state bags sont la réponse intégrée.
Cet article couvre les types de state bags, comment les écrire et les lire, comment réagir aux changements, et quand un événement reste le meilleur choix.
Qu'est-ce qu'un state bag
Un state bag est un magasin ressemblant à une table de clés et de valeurs attaché à quelque chose. Le runtime le synchronise pour vous, y compris pour les joueurs qui se connectent après que la valeur ait été définie. FiveM vous en propose trois types :
| Bag | Lire et écrire avec | Existe sur |
|---|---|---|
| Une entité | Entity(entity).state |
L'entité (ped, véhicule, objet) |
| Un joueur | Player(source).state sur le serveur, LocalPlayer.state sur le client |
Le joueur |
| Le serveur | GlobalState |
Le serveur entier |
Dans tous les cas, vous lisez une valeur comme un champ normal :
local locked = Entity(vehicle).state.lockedÉcrire depuis le serveur
Sur le serveur, définir une valeur la réplique aux clients qui peuvent voir ce bag :
-- server
local veh = CreateVehicle(`adder`, 215.0, -810.0, 30.7, 90.0, true, true)
Entity(veh).state.locked = true
Player(source).state.cuffed = true
GlobalState.weather = 'RAIN'Vous pouvez utiliser le style de champ ou l'appel explicite, ce qui vous permet également de choisir si la valeur est répliquée :
Entity(veh).state:set('locked', true, true):set(key, value, replicated) prend la clé, la valeur et un booléen. Avec true, la valeur est envoyée de l'autre côté, avec false, elle reste du côté qui l'a écrite.
Conseil : Les valeurs peuvent être des nombres, des chaînes, des booléens et des tables. Une table est copiée quand vous la définissez, donc modifier un champ dans la table que vous relisez ne fait rien. Définissez la table entière à nouveau pour la mettre à jour.
Écrire depuis le client
Un client peut écrire sur son propre joueur et sur les entités qu'il contrôle, mais une affectation simple reste locale :
-- client
LocalPlayer.state:set('inService', true, true) -- replicated to the server
LocalPlayer.state.hasMask = true -- local onlyLe serveur peut alors lire Player(source).state.inService. Considérez-le comme non approuvé : un client modifié peut écrire n'importe quelle valeur. Utilisez l'état écrit par le client pour la commodité, comme les drapeaux d'interface utilisateur, et conservez tout ce qui a un avantage attaché (argent, emploi, permissions) sur le serveur.
GlobalState est l'opposé : le serveur l'écrit, chaque client le lit, et les clients ne peuvent pas le changer pour les autres.
-- client
local weather = GlobalState.weatherRéagir aux changements
Au lieu d'interroger une valeur dans une boucle, enregistrez un handler qui s'exécute quand une clé change. C'est la raison principale d'utiliser les state bags côté client :
-- AddStateBagChangeHandler(keyFilter, bagFilter, handler)
AddStateBagChangeHandler('locked', nil, function(bagName, key, value, reserved, replicated)
local entity = GetEntityFromStateBagName(bagName)
if entity == 0 then return end
SetVehicleDoorsLocked(entity, value and 2 or 1)
end)keyFilterest la clé qui vous intéresse, ounilpour toutes les clés.bagFilterlimite le bag, par exemple un bag de joueur spécifique, ounilpour tous.bagNameressemble àentity:1234,player:5ouglobal.GetEntityFromStateBagNametransforme un bag d'entité en un handle, etGetPlayerFromStateBagNamefait de même pour un bag de joueur.
Sur le client, l'entité peut ne pas exister encore quand le handler s'exécute, par exemple pour un véhicule qui est encore en train de charger. GetEntityFromStateBagName peut retourner 0 dans ce cas, donc vérifiez-le. Relire l'état quand l'entité charge est la bonne habitude.
La même fonction existe sur le serveur, où elle est pratique pour réagir à un client écrivant une valeur répliquée.
State bags ou événements
| Utilisez un state bag quand | Utilisez un événement quand |
|---|---|
| La valeur décrit l'état actuel de quelque chose (verrouillé, menotté, en service) | Quelque chose s'est passé une fois (un achat, un coup, une pression sur bouton) |
| Les joueurs qui rejoignent plus tard doivent voir la valeur | Seuls les joueurs présents maintenant ont besoin de savoir |
| Beaucoup de joueurs doivent la lire à tout moment | Un joueur doit être informé de quelque chose |
| Vous interrogeriez sinon dans une boucle | Vous devez transmettre un résultat ou une charge utile ponctuelle |
Une bonne règle : les événements sont pour les actions, les state bags sont pour l'état. Si un joueur qui rejoint à mi-chemin aurait besoin d'être informé de la même chose à nouveau, c'est de l'état. Voir événements client et serveur pour l'autre côté.
Erreurs courantes
- L'utiliser pour des données volumineuses ou qui changent rapidement. Chaque changement est envoyé sur le réseau. N'écrivez pas une position dans un state bag à chaque frame.
- Faire confiance aux écritures du client. Validez tout ce qu'un client définit avant d'agir dessus sur le serveur.
- S'attendre à ce que le handler s'exécute avant que l'entité existe. Vérifiez le handle et relisez l'état quand l'entité apparaît.
- Muter une table en place. Lire
state.items, y ajouter et ne pas la définir à nouveau ne change rien. - Oublier la réinitialisation. L'état sur une entité disparaît avec l'entité, mais l'état sur un joueur ou
GlobalStatepersiste jusqu'à ce que vous le effiez, donc définissez-le ànilquand vous avez terminé.
Player(source).state.cuffed = nilListe de contrôle
| Symptôme | Correction |
|---|---|
| Le client définit une valeur mais le serveur ne la voit jamais | Utilisez :set(key, value, true) pour qu'elle soit répliquée |
Le handler s'exécute mais le handle d'entité est 0 |
L'entité n'a pas encore chargé : vérifiez et relisez l'état plus tard |
| Les joigneurs tardifs manquent la valeur | Utilisez un state bag au lieu d'un événement |
| Le changement de table ne se synchronise pas | Définissez la table entière à nouveau avec :set |
Ne peut pas changer GlobalState depuis le client |
Seul le serveur l'écrit : envoyez un événement et laissez le serveur le définir |
| L'ancienne valeur reste après que le travail soit fait | Définissez la clé à nil |
Réponses rapides
Qu'est-ce qu'un state bag dans FiveM ?
Un magasin de clés et de valeurs attaché à une entité, un joueur ou le serveur entier. Les valeurs que vous définissez sont synchronisées de l'autre côté par le jeu, donc vous n'avez pas besoin de déclencher un événement pour chaque changement.
Un client peut-il modifier un state bag et faire en sorte que le serveur le voie ?
Uniquement si la valeur est définie avec la réplication activée, par exemple LocalPlayer.state:set('key', value, true). Une affectation simple côté client reste locale, et le serveur ne devrait jamais la faire confiance pour quoi que ce soit d'important.
Les state bags ont-ils besoin de OneSync ?
Les state bags d'entité dépendent de OneSync, que les serveurs actuels utilisent par défaut. L'état du joueur et GlobalState sont des cas plus faciles pour commencer.
Des scripts sans ce problème
Parrots as PetsQuatorze oiseaux qui se posent sur votre épaule, volent avec vous et reviennent quand vous les appelez.Voir le script →
Advanced BoostingDu boosting de véhicules piloté par tablette : contrats de classe D à S+, crews et file d’attente en direct.Voir le script →
Hookah SystemDes chichas et du mobilier lounge à placer, avec plus de 50 saveurs et effets.Voir le script →