FiveM CreateThread et Wait(0) expliqués : pourquoi votre script coûte des ms
Comment CreateThread, Wait et SetTimeout fonctionnent dans Lua FiveM, pourquoi Wait(0) dans chaque boucle apparaît dans resmon, et pourquoi une boucle while sans Wait gèle le jeu.
Vous écrivez une boucle pour vérifier quelque chose, et le jeu cesse de répondre, ou resmon montre votre resource à quelques ms alors que vous êtes immobile :
while true do
-- no Wait here: the game never gets control back
local ped = PlayerPedId()
endCet article explique comment les threads et Wait fonctionnent dans Lua FiveM, ce que Wait(0) coûte vraiment, et comment écrire des boucles qui restent peu coûteuses.
Comment les threads fonctionnent dans FiveM
Lua dans FiveM est coopératif. Votre script ne s'exécute pas en parallèle avec le jeu : il s'exécute quand le runtime l'appelle, et il doit redonner le contrôle. Deux choses rendent cela possible :
- Les gestionnaires d'événements et les callbacks s'exécutent une fois, du début à la fin, puis retournent.
- Les threads créés avec
CreateThreadsont de petites coroutines que le planificateur reprend encore et encore. Un thread redonne le contrôle quand il appelleWait.
CreateThread(function()
while true do
Wait(1000)
print('one second later')
end
end)CreateThread et Citizen.CreateThread sont la même fonction, et il en va de même pour Wait et Citizen.Wait. Utilisez celui que vous préférez, mais soyez cohérent dans un script.
Une boucle while sans Wait gèle le jeu
Si un thread n'appelle jamais Wait, le planificateur n'obtient jamais le contrôle. Sur le client, le jeu cesse de dessiner les frames jusqu'à ce que la boucle se termine, et sur le serveur, chaque autre script attend le vôtre.
CreateThread(function()
while true do
-- missing Wait: this blocks the whole client
end
end)La même chose se produit avec une boucle repeat … until ou une longue boucle for qui effectue un travail lourd. Chaque boucle qui peut s'exécuter pendant un certain temps a besoin d'un Wait à l'intérieur. Si un script gèle le jeu au démarrage, cherchez d'abord une boucle comme celle-ci.
Ce que coûte Wait(0)
Wait(0) signifie : pause jusqu'à la frame suivante (client) ou le tick suivant (serveur), puis continue. La boucle s'exécute à nouveau à chaque frame, donc à 60 fps, son corps est payé 60 fois par seconde.
CreateThread(function()
while true do
Wait(0)
local coords = GetEntityCoords(PlayerPedId())
local dist = #(coords - vector3(215.0, -810.0, 30.7))
if dist < 3.0 then
-- draw a marker, show text...
end
end
end)Une boucle comme celle-ci coûte peu. Dix d'entre elles, réparties sur trente resources, s'accumulent à plusieurs ms de temps client, ce qui est exactement ce que resmon montre comme ms élevé. Le coût n'est pas le Wait(0) lui-même, c'est tout ce que vous répétez à chaque frame.
Wait uniquement aussi court que vous en avez besoin
La plupart des vérifications n'ont pas besoin de s'exécuter à chaque frame. Demandez-vous à quoi sert la boucle :
| Ce que fait la boucle | Attente raisonnable |
|---|---|
| Dessiner un marqueur ou du texte, gérer une pression de touche | 0 (seulement quand le joueur est proche) |
| Vérifier une distance pour décider si quelque chose est proche | 250 à 1000 |
| Interroger un état qui change rarement (job, véhicule) | 1000 ou plus, ou utiliser un événement |
L'astuce habituelle est deux vitesses : une boucle lente qui vérifie la distance, et une rapide uniquement quand le joueur est à l'intérieur de la zone.
local zone = vector3(215.0, -810.0, 30.7)
CreateThread(function()
while true do
local sleep = 1000
local dist = #(GetEntityCoords(PlayerPedId()) - zone)
if dist < 10.0 then
sleep = 0
DrawMarker(1, zone.x, zone.y, zone.z - 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
1.5, 1.5, 0.5, 255, 255, 255, 120, false, false, 2, false, nil, nil, false)
end
Wait(sleep)
end
end)Loin de la zone, le thread se réveille une fois par seconde. Près d'elle, il dessine à chaque frame. Resmon tombe à presque rien pour la plupart des joueurs.
Conseil : avant de chercher une boucle plus rapide, demandez-vous si un événement ou un state bag peut vous dire quand la valeur change. Pas de boucle du tout est moins cher que n'importe quel Wait.
SetTimeout : exécuter quelque chose une fois, plus tard
Si vous n'avez besoin de faire quelque chose qu'une seule fois après un délai, vous n'avez pas besoin d'un thread avec un Wait :
SetTimeout(5000, function()
print('five seconds later')
end)SetTimeout(ms, callback) exécute le callback une fois après le délai et ne bloque rien. Il existe aussi sur le serveur. Pour une tâche répétitive, un thread avec Wait est le bon outil, puisque SetTimeout ne se répète pas d'lui-même.
Attendre à l'intérieur des gestionnaires d'événements
Un gestionnaire d'événements peut aussi appeler Wait, car FiveM l'exécute dans sa propre coroutine. Soyez prudent sur le serveur : après un Wait, le source global ne pointe plus vers le joueur qui a déclenché l'événement. Sauvegardez-le d'abord (voir événements client et serveur):
RegisterNetEvent('my_script:server:buy', function()
local src = source
Wait(500)
print(('player %s bought something'):format(src))
end)Arrêter un thread
Un thread se termine quand sa fonction retourne. Pour arrêter une boucle de l'extérieur, utilisez un drapeau que la boucle vérifie :
local running = true
CreateThread(function()
while running do
Wait(500)
-- work
end
end)
AddEventHandler('onResourceStop', function(resource)
if resource == GetCurrentResourceName() then
running = false
end
end)Tous les threads d'une resource s'arrêtent d'eux-mêmes quand la resource s'arrête, donc vous n'avez besoin de cela que pour arrêter une boucle pendant que la resource continue de s'exécuter.
Liste de contrôle
| Symptôme | Correction |
|---|---|
| Le jeu gèle quand un script démarre | Une boucle while true do n'a pas de Wait : en ajouter un |
| La resource montre des ms constantes dans resmon au repos | Remplacer Wait(0) par une vérification de distance et une attente plus longue |
| Besoin d'exécuter quelque chose une seule fois après un délai | SetTimeout(ms, fn) au lieu d'un thread |
source est faux après un Wait sur le serveur |
Sauvegarder local src = source avant le Wait |
| Interroger une valeur qui change rarement | Utiliser un événement ou un state bag au lieu d'une boucle |
Citizen.Wait ou Wait ? |
Même fonction, choisissez un style |
Réponses rapides
Que fait Wait(0) dans FiveM ?
Il pause le thread actuel jusqu'à la frame suivante sur le client, ou le tick serveur suivant. La boucle s'exécute ensuite à chaque frame, donc tout à l'intérieur coûte 60 fois ou plus par seconde.
Citizen.Wait est-il différent de Wait ?
Non. Wait et Citizen.Wait sont la même fonction, et il en va de même pour CreateThread et Citizen.CreateThread. Les noms courts ne sont que des alias.
Est-ce que Wait arrête le jeu entier ?
Non. Il ne pause que le thread qui l'appelle, et les autres threads et le jeu continuent de s'exécuter. Une boucle qui n'appelle jamais Wait est ce qui bloque tout.
Des scripts sans ce problème
Advanced BoostingDu boosting de véhicules piloté par tablette : contrats de classe D à S+, crews et file d’attente en direct.Voir le script →
CCTV Security CamerasDes caméras à placer, une tablette multi-vues en direct et des photos imprimées comme preuves.Voir le script →
Mic PhoneUn téléphone pliable qui se déplie en tablette et se prolonge jusqu’au vrai téléphone du joueur.Voir le script →