How would you do timed rewards

As I am making a level system where you get 1 level per hour, would having a wait() with how much seconds is in an hour better or use time.os()?

Using while true do loop should be good enough because your wait(n) is not going to break but using time.os() is more sufficient way to do it and it’s a little bit more complicated than the first example. You can use this quick example anyway:

local Seconds = 0
local Hours = 0

while true do
    if Seconds >= 3600 then
       Hours = Hours + 1
       Seconds = 0
    end
    Seconds = Seconds + 1 
    wait(1)
end

What you could do is save how much time has past since the player has received their last reward or since they first joined.
When a player joins, you can fire a remote that will send the timestamp of when they receive the reward to the client.
When this timer is over, the client fires a remote to the server.
On the server you can then verify if the client is allowed to claim their reward.
If it’s not allowed you update the timer and you could tell the player that the timer is out of sync.
If it is allowed, you reward the player. And update the timeData of that player.
This makes it so you don’t need to use a while loop on the server, only on the client.

It would look something like this:

-- Server Code
local Players = game:GetService("Players")
local timeData = {}

Players.PlayerAdded:Connect(function(player)
    local data = getDataFromDatastore()
    local timeJoined = tick()
    local timeSinceReward = data.timeSinceReward
    someRemote:FireClient(timeJoined + 3600 - timeSinceReward)
    timeData[tostring(player.UserId)] = {
        joined = timeJoined,
        playTime = timeSinceReward
    }
end)

anotherRemote.OnServerEvent:Connect(function(player)
    local data = timeData[tostring(player.UserId)]
    if tick() - data.joined > 3600 then
        -- Reward player then reset the timer, update the timer using a remote.
        timeData[tostring(player.UserId)] = {joined = tick(), playTime = 0}
        someRemote:FireClient(tick() + 3600)
    else
        -- Tell client the timer is out of sync, update this timer using a remote.
    end
end)

-- Client Code

local fired = false
someRemote.OnClientEvent:Connect(function(rewardTimestamp)
    while not fired do
        if tick() >= rewardTimestamp then
            fired = true
            anotherRemote:FireServer()
        end
        wait(1)
    end
end)
1 Like

If you’re going to do it this way, then you should be handling all time values in seconds and making calculations based off of that accumulated time rather than manually counting up this way.

local accumulatedTime = 0

while true do
    accumulatedTime = accumulatedTime + 1
    wait(1) -- Opt for a Heartbeat-based wait instead
end

Didn’t use elapsedTime otherwise there’d be some variable shadowing. Anyhow: the number of seconds would be accumulatedTime%60 and minutes would be accumulatedTime/60. You can check if this time reaches a certain threshold before resetting it and granting rewards.