
The Root Causes
High MSPT (milliseconds per tick): When the server takes longer than 50ms per tick, entity state updates lag. Players receive stale position data and see each other at wrong positions.
Network congestion: If your server uplink is saturated (100+ players on a 100 Mbps uplink), packet delivery timing becomes inconsistent.
Script blocking: A Lua script that calls Wait(0) in a tight loop without yielding blocks the main thread. All entity updates queue behind it.
Database query bottleneck: A slow synchronous database query (blocking the main script thread) pauses all state updates while waiting.
Diagnosing Your Specific Desync
Open the server console and observe MSPT. On Space-Node, this is visible in the Pterodactyl panel metrics:
Server MSPT should be consistently below 50ms.
Occasional spikes to 100ms are acceptable.
Sustained MSPT above 100ms = diagnosed lag causing visible desync.
Use the resmon command in-game (as admin) to see which resources consume the most MSPT:
/resmon
Resources showing >5ms average MSPT on a healthy server are candidates for optimisation.
The Most Common Script-Level Fix
Blocking the native thread is the most frequent cause of performance-induced desync:
-- WRONG - blocks main thread with heavy computation:
Citizen.CreateThread(function()
while true do
for i = 1, 10000 do
-- heavy calculation
end
Wait(0) -- waits but still on main thread
end
end)
-- RIGHT - use async where possible, yield appropriately:
Citizen.CreateThread(function()
while true do
-- lightweight polling
Wait(1000) -- yield for 1 second between checks
end
end)
Network Optimisation
For servers with 64+ players, ensure your uplink can handle the bandwidth:
64 players × ~0.8 Mbps per player = ~50 Mbps sustained. A 100 Mbps uplink at 50% utilisation begins introducing queuing latency. Space-Node provides 1 Gbps uplinks on all FiveM plans, eliminating network bottlenecks even at 128-player loads.