FiveM resmon high ms: how to find and fix slow scripts
A resource shows high ms in resmon? How to read resmon 1, why Wait(0) loops cost so much, and how to sleep when far away, cache the ped and use ox_lib points and zones.
You open the resource monitor and one script shows this:
my_script 0.35 msAn idle script should be near zero. A script that eats a third of a millisecond, or much more, on every frame makes your game stutter, and the players with weaker computers feel it first. This guide shows how to see which resource is the problem and the changes that bring it down.
Open the resource monitor
In the F8 console of the game, type:
resmon 1An overlay shows every client resource with its CPU time (ms) per frame and its memory. Type resmon 0 to close it. Check these things:
- Stand still, in a quiet place, and read the list. The numbers that matter are the ones that stay high when you do nothing.
- Look at who is at the top. A resource that goes up only near its own marker or shop is normal. One that is high everywhere is not.
- Watch it a few seconds. A single peak when a script starts is harmless; a constant value is the problem.
The ms is time on the client. Server performance is a separate topic: see server thread hitch warning.
Cause 1: a loop with Wait(0)
The most common reason for a high value is a loop that runs every frame, all the time:
CreateThread(function()
while true do
Wait(0)
local ped = PlayerPedId()
local coords = GetEntityCoords(ped)
local dist = #(coords - vec3(215.0, -810.0, 30.7))
if dist < 3.0 then
DrawText3D(215.0, -810.0, 30.7, 'Press E')
end
end
end)This runs the natives 60 times a second, even when the player is on the other side of the map. Most of the time the answer is "nothing to do", and that cost is wasted.
Fix 1: sleep when you are far away
Let the loop sleep longer when nothing is close, and only run every frame when the player is near:
CreateThread(function()
local target = vec3(215.0, -810.0, 30.7)
while true do
local sleep = 1000
local coords = GetEntityCoords(PlayerPedId())
local dist = #(coords - target)
if dist < 10.0 then
sleep = 0
if dist < 3.0 then
DrawText3D(target.x, target.y, target.z, 'Press E')
end
elseif dist < 50.0 then
sleep = 250
end
Wait(sleep)
end
end)The loop checks once per second when you are far away, which costs almost nothing, and runs every frame only inside the 10 metre area where it draws. Use #(a - b) with vec3 values for the distance: it is faster than the older GetDistanceBetweenCoords native.
Fix 2: cache values you reuse
Calling a native every frame to get a value that rarely changes is a waste. PlayerPedId() is the classic one. Store it once and refresh it only when it can change:
local ped = PlayerPedId()
CreateThread(function()
while true do
Wait(500)
ped = PlayerPedId()
end
end)If you use ox_lib, it does this for you. cache.ped is kept up to date, and cache.vehicle and cache.serverId work the same way:
local coords = GetEntityCoords(cache.ped)lib.onCache lets you react when a cached value changes, without checking in a loop.
Fix 3: stop calling natives every frame
Check the body of the loop and ask for each line: does this really have to run every frame? Move out what does not.
- Drawing (text, markers, sprites) has to run every frame, but only while it is visible.
- Reading data (job, money, inventory) does not: read it on an event, and keep it in a variable.
- Searching (
GetClosestVehicle,GetGamePool, loops over all players) is expensive. Run it once every second or two, not every frame. - Do not create things in a loop: creating blips, text entries or tables every frame fills memory and makes the garbage collector work.
-- run once per second
local closest = nil
CreateThread(function()
while true do
closest = GetClosestVehicle(GetEntityCoords(cache.ped), 5.0, 0, 70)
Wait(1000)
end
end)Fix 4: use ox_lib points and zones instead of loops
Most "am I near this spot" loops are not needed. ox_lib can do the distance work for you, and only runs your code when it matters.
A point calls a function when you enter or leave a radius, and one every frame only while you are inside:
local point = lib.points.new({
coords = vec3(215.0, -810.0, 30.7),
distance = 10.0,
})
function point:onEnter()
print('entered')
end
function point:onExit()
print('left')
end
function point:nearby()
DrawText3D(self.coords.x, self.coords.y, self.coords.z, 'Press E')
if self.currentDistance < 2.0 and IsControlJustReleased(0, 38) then
print('used')
end
endZones do the same for shapes such as a sphere, a box or a polygon:
local zone = lib.zones.sphere({
coords = vec3(215.0, -810.0, 30.7),
radius = 3.0,
onEnter = function() print('in') end,
onExit = function() print('out') end,
})For interaction with entities and spots, a target resource (ox_target, for example) replaces the "press E" loop in the same way. Check the ox_lib documentation for the exact options of each.
Check the result
Run resmon 1 again. A fixed script should sit near 0.00 ms when you stand away from its spots, and rise only when you are near them. If it is still high, look for a second loop in the same resource: one Wait(0) is often not the only one.
Warning: do not set a long sleep in a loop that handles a key press or a drawing. If you sleep 500 ms and the player presses the key, the script will miss it. Sleep only when you know nothing is going on, and drop to
Wait(0)when something is.
Checklist
| Symptom | Fix |
|---|---|
| High ms when idle | Find the while true do Wait(0) loop and add a distance-based sleep |
PlayerPedId() in every frame |
Cache it, or use cache.ped from ox_lib |
| Distance checks in a loop | Replace with lib.points.new or lib.zones |
| Searching entities each frame | Run it once per second and keep the result |
| Reading player data in a loop | Read it on the framework event and store it |
| Missed key presses after adding a sleep | Use Wait(0) only while the player is near |
Quick answers
What is a good resmon value for a script?
A script that is idle should sit around 0.00 to 0.01 ms. Anything that stays well above that when you are not using it has a loop that works too hard.
What does Wait(0) do and why is it costly?
Wait(0) runs the loop once every frame. At 60 frames per second that is 60 runs per second, and any native you call in it is called 60 times per second.
Do I need ox_lib to fix high ms?
No. The fixes work in plain Lua. ox_lib only makes them shorter, with cache.ped, lib.points and lib.zones.
Scripts that skip this problem
CCTV Security CamerasPlaceable cameras, a live multi-view tablet and printed evidence photos.View script →
Advanced BoostingTablet-driven vehicle boosting: contracts from class D to S+, crews and a live queue.View script →
Arcade MachinesSeven playable arcade games in real cabinets, with leaderboards and bets.View script →