What's Wrong With My Hourly Chest System?

I’m trying to create a chest system which should reset every 6 hours. I’m not sure if it’s not data saving, or there’s something wrong with my script but it doesn’t seem to save the time once the player leaves?

Serverscript

local DS = game:GetService("DataStoreService")
local RS = game:GetService("ReplicatedStorage")

function DailyReward(v)
	
	local Player = game.Players:GetPlayerByUserId(v)
	
    local MainCurrency = Player:WaitForChild("MainCurrency")
	
	local Gems = MainCurrency:FindFirstChild("Gems")
	
	if MainCurrency then
		
		Gems.Value = Gems.Value + 15 
	
	end
end



game.ReplicatedStorage.Chests.GChest.OnServerEvent:Connect(function(plr, mode)
		
     if (mode == "Group_R") then
		print("Mode Correct")
		if plr:IsInGroup(6154443) then 
	         local Data = DS:GetDataStore("DailyRewards_V.1", plr.UserId)
	         local timeleft = Data:GetAsync("TimeLeft")
	         local hours = os.time() + 10800 --Time in seconds, default is 24 hours
			 warn(timeleft, hours, Data)
	         if timeleft == nil then
	             Data:SetAsync("TimeLeft", hours)
	             Data:IncrementAsync("Amount", 1) --Amount of times the player got the reward
	             DailyReward(plr.UserId)
	         else
	             local timeleft = tonumber(timeleft)
	             if (timeleft > 0 and timeleft - os.time() > 0) then
	                 DailyReward(plr.UserId)
             	end
	         end
     	end
	end
end)

--[[game.ReplicatedStorage.Chests.GetTime.OnServerInvoke = function(player)
	
	warn("Invoked")
	
	if player:IsInGroup(6154443) then 
		
		local Data = DS:GetDataStore("DailyRewards_V.1", player.UserId)
		local timeleft = Data:GetAsync("TimeLeft")
		local timeleft2 = tonumber(timeleft)
		
	    local hours = os.time() + 10800
		
		warn("Sending Time")
		
		return hours, timeleft
		
	end
	
end]]--

game.ReplicatedStorage.Chests.GetTime.OnServerInvoke = function(player) 
	
	local Data = DS:GetDataStore("DailyRewards_V.1", player.UserId)
	local timeleft = Data:GetAsync("TimeLeft")
	local hours = os.time() + 10800 
	
	warn(timeleft, hours, Data)
	return hours, timeleft, Data
	
end

The remote event sends the time to the player and the remote event gives the reward if the time is right. The thing is that the time sent is always 3:00:00.

Localscript which displays the time

local target, timeleft = game.ReplicatedStorage.Chests.GetTime:InvokeServer()

local function toHMS(s)
	return ("%02i:%02i:%02i"):format(s/3600, s/60%60, s%60)
end

while wait(1) do
	
	if timeleft == nil then
		script.Parent.Text = "Claim"
	else
		script.Parent.Text = toHMS(target - os.time())
	end
		
end

I have no clue what I’m doing wrong, or there is an error?

Output

1590196302 1590196652 DailyRewards_V.1
1590196652 1590196302
1 Like

I tried saving the data when the player leaves, but it doesn’t seem to effect anything?

Having a quick scan I believe you’re comparison of ‘timeleft’ is wrong.

You’re storing a number, which unless you are setting it to NIL somewhere else, should always return a number.

Add a print when you get timeleft, reduce your 6 hours to 1 minute or less and see what data you’re getting back.

I think I found an easier way, I’m just going to store the timer inside a folder in the player. When I need to display it I’ll use my minutes to hours:minutes:seconds function. It seems a lot easier

Okay so this works in theory but in practice you’re gunna run into a lot of problems. Here’s the thing, os.time() and tick() are both accurate ways to measure time on instance are a time. But in reality they are NOT and accurate measure for long periods of time where a player is expected to leave and rejoin the game at some point. When a player joins again, the time value could be entirely different based on the server region they happen to connect to. The only way to really verify this perfectly across all servers is by using something called unixtime. This is essentially the same thing except the way you should go about getting it is by using apis in your game to find the real world time right away. This means diving into some HTTPService stuff.

I have done a daily reward system before, and actually it’s not that hard. What I did was I saved the previous os.time() value when the received their last reward and compared it to the current time. If the current time is greater than the saved time + (24*3600) then I would save the current time and give them the reward

Posted code if anyone searches it up in the future

local code = 'AAA' --Change to reset the daily reward thing
local DSS = game:GetService("DataStoreService")
local dailyRewardSystem = DSS:GetDataStore('dailyReward_'..code)

local hoursUntil = 24

game.Players.PlayerAdded:Connect(function(player)
	
	local dailyRewardFolder = Instance.new("Folder",player)
	dailyRewardFolder.Name = 'Rewards'
	local timeUntilStuff = Instance.new("IntValue",dailyRewardFolder)
	timeUntilStuff.Name = 'DailyReward'
	
	local timeUntil 
	local success, err = pcall(function()
		timeUntil = dailyRewardSystem:GetAsync(player.UserId)  or 0
	end)
	
	if success then
		timeUntilStuff.Value = timeUntil - os.time()
        print((timeUntil) - os.time())
	end
	
end)


local function checkIfCanRecieve(player)
	local dr = player:FindFirstChild('Rewards'):WaitForChild('DailyReward')
	if dr then
		if dr.Value + os.time() < os.time() then
			print('Yes')
			print(os.time(),os.time()+3600*hoursUntil)
			dr.Value = 3600*hoursUntil
			dailyRewardSystem:SetAsync(player.UserId, os.time()+(3600*hoursUntil))
		end
	end
end


wait(2)

checkIfCanRecieve(game.Players:FindFirstChildOfClass("Player"))

while wait(1) do 
   for i,v in ipairs(game.Players:GetPlayers()) do 
      if v:FindFirstChild('Rewards'):FindFirstChild('DailyReward') then
         v:FindFirstChild('Rewards'):FindFirstChild('DailyReward').Value = v:FindFirstChild('Rewards'):FindFirstChild('DailyReward').Value - 1
      end
   end
end

Nevermind that sounds like a horrible idea because they’d have to play a long time o - o