Ok, this issue is rather specific and complicated. But here is an overview:
My shooter game uses a custom wait function for accurate yielding (such as weapon fire-rate pauses).
local function yield(duration, stopCondition)
local frame = 0
local frameToWait
local elapsed = 0
local t= os.clock()
warn("======start", t)
repeat
local _,dt = game:GetService("RunService").Stepped:Wait()
elapsed += dt
frame += 1
frameToWait = roundNumber(duration / (elapsed / frame), 0)
warn(os.clock()-t)
until frame >= frameToWait
or elapsed >= duration
or (stopCondition and stopCondition())
warn("yield frame waited", frame)
print("total elapsed", elapsed)
print(os.clock() - t)
return elapsed - duration
end
E.g: When yield(0.05) is called, the function halts and returns after ~3 frames (in 60 FPS) to wait for 0.05 seconds.
Here is the output of yield(0.05)
, in which the highlighted part is (os.clock() - t)
calculated per frame, which t is the initial os.clock()
recorded. For example, the first highlighted line shows that it has been ~0.163 seconds after the first frame, then 0.034
for the second frame, and 0.049
seconds for the last.
The Problem:
However, if I run yield(0.05) after a UserInputService
mouse click listener (which is practically used in my game), the value of os.clock() - t
shows a significantly low amount of time.
game:GetService("UserInputService").InputBegan:Connect(function(inp)
if inp.UserInputType == Enum.UserInputType.MouseButton1 then
yield(0.05)
end
end)
And the final time calculated by os.clock() - t
after waiting for 0.05 seconds is 0.034 (shown in the last line), which is inaccurate. U:sing tick()
/ time()
results in the same behavior.
Why is this important to me?
The deltaTime
returned by RunService.Stepped
seems to be unaffected. However, I am currently trying to fire a remote 3 times on every 0.05 seconds (e.g: Firing a weapon). The server keeps track and verify the timing between these remotes for security purposes.
Here is a client clicking their mouse, then firing the remote 3 times, each time with an interval of 0.038 seconds.
game:GetService("UserInputService").InputBegan:Connect(function(inp)
if inp.UserInputType == Enum.UserInputType.MouseButton1 then
for i = 1,3 do
game.ReplicatedStorage.RemoteEvent:FireServer()
yield(0.038)
end
end
end)
And here is the output on the server: Each number represents the delta time between each time when the remote is received. I assume due to server tick rates, the best interval that the server can detect should be ~0.033 seconds, which is good enough for me.
However, as seen in the output below, due to the “mouse click halting behavior”, the second remote is received in 0.015 second after the first one was received.
This inconsistency result has caused me some problems regarding on the security mesaures that I am trying to implement, considering the value is ~50% off.
This is not a networking issue as I ran few tests withOUT any user mouse click input, I am able to produce consistent result.
The only solution that I can find of is to add task.wait()
before yielding 0.05. But this eliminates my goal of having a precise waiting function and it is not feasible to introduce such input delay in a shooter game.
Is there any other solutions / addresses can be made to this problem?