Server Lag (specifically finding an alternative to wait())

Recently, I have been developing a fighting game. The problem is, over time, my servers keep building up lag. When that happens, usually my wait() times get extremely long. I’ve tried to make a custom wait() function, but all it did was cause more memory to be used.

This is an image from the game’s server list. I shut down all servers and recorded the time it took for the servers to lag. Not surprisingly, it took only 1 hour for them all to become extremely slow.

Since the core of the entire gameplay revolves around timed-effects, wait()s are used all the time.

local function AccurateWait(seconds)
if not seconds then
seconds = 0
local last = tick()
local secondsPassed = 0
local connection = RunService.Heartbeat:Connect(function(dt)
secondsPassed = secondsPassed + dt
until secondsPassed >= seconds

I wasn’t sure if the Wait() function was the underlying problem. Everything in my code seems to work perfectly well. So I changed it to this, and it worked flawless for the first few minutes, but started to lag after that.

Also in my code I have coroutines running
I don’t use spawn(function(), instead I use a coroutine to create it’s own separate thread from the code.
So I would have

local loop = coroutine.create(function()

and then


However, I am pretty sure the coroutines wouldn’t be causing the lag. All loops I start in the code, I give it some specific condition for it to stop and break (yes, I checked every single one). And this lag started after I had already implemented the coroutines.

Also to mention, all of this code is in a ModuleScript, and it’s only required 2 different times by server scripts.

Yet this module script (SpellCasting) causes 50% of lag??

If anyone could give me any idea of where this lag could be coming from… please… I’ve been working to optimize the script for 3 days now and nothing is fixing it…


If the lag steadily grows in your server, then maybe it could be an underlying problem with not stopping your loops correctly, possibly?

If the SpellCasting script is the root cause of lag, perhaps you should show the code (particularly the loops) of the script?

Have you tried the microprofiler? It allows you to see exactly how much time individual functions take to run in your scripts. I don’t recall if you can see server info from the client though even if you’re the creator. You should be able to in Studio though.

Here are some examples of the loops I use in my code:

local loop = coroutine.create(function()
local last = tick()
while symbol do
local t = tick()
local rot = t - last
last = t
symbol.CFrame = symbol.CFrame * CFrame.Angles(0, 0, rot)

Symbol is a part. Once it is finished it’s lifetime, it is effectively destroyed.

for i = 0, 20 do
local loop = coroutine.create(function()
local effect = game.ReplicatedStorage.FX.Sphere:Clone()
effect.Size =, 0, 0)
effect.CFrame =
effect.Color = Color3.fromRGB(255, 255, 255)
effect.Transparency = 0.8
effect.CFrame = effect.CFrame * CFrame.Angles(math.rad(math.random(-30, 30)), math.rad(math.random(-30, 30)), math.rad(math.random(-30, 30)))
effect.Parent = workspace.Parts
effect.CanCollide = false
local properties = {
CFrame = effect.CFrame * CFrame.Angles(math.rad(math.random(-30, 30)), math.rad(math.random(-30, 30)), math.rad(math.random(-30, 30))),
Size =, 70, 70)
local tweenInfo =, Enum.EasingStyle.Back, Enum.EasingDirection.Out, 0, false, 0)
local tween = TweenService:Create(effect, tweenInfo, properties)
local properties = {
Size =, 0, 0)
local tweenInfo =, Enum.EasingStyle.Quad, Enum.EasingDirection.Out, 0, false, 0)
TweenService:Create(effect, tweenInfo, properties):Play()

I also use the coroutine here.

I am also seeing what the microprofiler can help me with.

Can you PLEASE use


instead of putting it in a quote please


Please just use this

local Heartbeat = game:GetService("RunService").Heartbeat
local function AccurateWait(seconds)
	local timestampTarget = tick() + (seconds or 0)
	until tick() > timestampTarget

Creating functions and connecting to events just to get the time passed is painfully slow.

Here’s another alternative, but be careful if you need to replace Heartbeat with Stepped because Stepped’s first result is not the time delta.

local Heartbeat = game:GetService("RunService").Heartbeat--$const
local function AccurateWait(seconds)
	seconds = seconds or 0
		seconds = seconds - Heartbeat:Wait()
	until seconds < 0

What does the --$const comment do?

Oops accidentally left that in. It’s a “MetaComment” that I made up for developing in my game’s system. It means that my source simplifier can assume the statement has no side-effects, remove it if it’s unused, and give me an error if something in the script tries to set it. It also doesn’t need to look ahead to see if it’s set anywhere, which can improve processing times a bit for long scripts. It’s specific to what I do and is just habit at this point.


If you truly believe the problem is because of wait(), well then say no more. I’ve got a pretty accurate wait for you to use.

local function rWait(n)
    n = n or 0.05 — this default value here can be changed.
    local startTime = os.clock()

    while os.clock() - startTime < n do
        RunService.Stepped:Wait() — you’ll have to get RunService at the top of your code. (You should use a module.)

^— Benchmark with a 3 second wait.

You don’t need to use os.clock at all, because the Heartbeat, Stepped, and RenderStepped events of RunService return the amount of time waited:

local rs = game:GetService("RunService")

local function rWait(n)
	assert(typeof(n) == "number")
	local t = 0
	local dt
		_, dt = rs.Stepped:Wait()
		t = t + dt
	until (t >= n)
	return t
1 Like