After 4 years of running 64-slot servers I've collected a few patterns that consistently slash tick time. None of these are revolutionary, but combined they took our average resmon from 6-8ms to under 1.5ms.
1. Stop polling, start listening
The biggest offender is always a while true do Wait(0) loop watching for some state change. 90% of the time you can replace it with a stateBag handler:
AddStateBagChangeHandler('isCuffed', nil, function(bagName, _, value)
local ply = GetPlayerFromStateBagName(bagName)
if not ply then return end
-- react
end)
Zero polling, instant reaction. DO NOT put a Wait(0) in this handler — it runs on the resource thread.
2. Coalesce your draws
If you have 3 scripts each doing their own DrawText3D, you're paying 3× the matrix math. Centralize a single draw queue resource and let other scripts push entries.
3. Distance gate before entity lookup
GetClosestVehicle is expensive. Always wrap with a quick coord distance check:
local pc = GetEntityCoords(PlayerPedId())
local target = GetEntityCoords(somePed)
if #(pc - target) > 15.0 then return end
-- now do the heavy lookup
4. Cache PlayerPedId() per tick
Yes really. PlayerPedId() is cheap but called 50 times per frame in some scripts. Cache once at the top of your loop.
5. Use Citizen.Await instead of Citizen.CreateThread for one-shots
If your "thread" only runs once and exits, it doesn't need a thread. Promise-style is lighter.
What patterns do you guys use? I want to learn more.