1 day timer(daily rewards)

I wanted to make a daily reward system in an obby. But the problem is the timer. I don’t know how how to make the timer go on when the player is offline. How do I make it go when the player is offline?

This is a server script:

local DS = game:GetService("DataStoreService"):GetDataStore("Shut")

local function OnCharacterAdded(char)
	game:GetService("RunService").Stepped:Wait()
	local plr = game.Players:GetPlayerFromCharacter(char)
	char:WaitForChild("HumanoidRootPart").CFrame = workspace.Checkpoints[tostring(plr.TeleportedStage.Value)].CFrame + Vector3.new(0,3.25,0)
end

function OnPlayerAdded(plr)

	plr.CharacterAdded:Connect(OnCharacterAdded)

	local stats = Instance.new("Folder")
	stats.Name = "leaderstats"
	stats.Parent = plr
	
	local values = Instance.new("Folder")
	values.Name = "Values"
	values.Parent = plr

	local stage = Instance.new("IntValue")
	stage.Name = "Stage"
	stage.Parent = stats

	local TeleStage = Instance.new("IntValue")
	TeleStage.Name = "TeleportedStage"
	TeleStage.Parent = plr
	
	local Skips = Instance.new("IntValue")
	Skips.Name = "Skips"
	Skips.Parent = values
	
	local Time = Instance.new("IntValue")
	Time.Name = "Time"
	Time.Parent = values
	Time.Value = 900
	
	local daily = Instance.new("IntValue")
	daily.Name = "Daily"
	daily.Parent = values
	daily.Value = 86400
	
	local Coins = Instance.new("NumberValue")
	Coins.Name = "Coins"
	Coins.Parent = stats

	local key = "id_" .. plr.userId

	local data = DS:GetAsync(key)

	if data then
		stage.Value = data
		TeleStage.Value = stage.Value
	else
		DS:GetAsync(key, stage)
	end
	
	while wait(1) do
		if plr.leaderstats.Stage.Value >= 10 then
			plr:WaitForChild("leaderstats"):WaitForChild("Coins").Value += plr.leaderstats.Stage.Value / 100
		end
		daily.Value -= 1
	end
end

game.Players.PlayerAdded:Connect(OnPlayerAdded)

function OnPlayerRemoved(plr)
	local key = "id_" .. plr.userId
	local data = plr.leaderstats.Stage.Value
	DS:SetAsync(key, data)
end

game.Players.PlayerRemoving:Connect(OnPlayerRemoved)


local RS = game:GetService("ReplicatedStorage")

repeat wait() until script.Main ~= nil

local main = require(script.Main)

function recived(plr, direction)
	if direction == "up" then
		main.up(plr, direction)
	elseif direction == "down" then
		main.down(plr, direction)
	elseif direction == "doubleup" then
		main.doubleup(plr, direction)
	elseif direction == "doubledown" then
		main.doubledown(plr, direction)
	elseif plr.Stages > main.TOTAL_STAGES_IN_GAME then
		print("Player is in an older version of the game!")
	end
end

RS.StageTransfer.OnServerEvent:Connect(recived)


for _, v in pairs(workspace.Checkpoints:GetChildren()) do
	v.Touched:Connect(function(hit)
		if v ~= nil then
			if v.Parent == workspace.Checkpoints then
				local player = game:GetService("Players"):GetPlayerFromCharacter(hit.Parent)
				if player then
					if player.leaderstats.Stage.Value == tonumber(v.Name) - 1 then
						if player.leaderstats.Stage.Value < tonumber(v.Name) then
							player.leaderstats.Stage.Value = tonumber(v.Name)
						end
					end
					if player.leaderstats.Stage.Value >= tonumber(v.Name) then
						player.TeleportedStage.Value = tonumber(v.Name) --player.leaderstats.Stage.Value
					end
				end
			end
		end
	end)
end

I created a GUI for the player to claim. The script is:

-- 12/20 chance for 1k coins, 5/20 chance for 2.5k coins, and free skip(3/10 chance)
game.Players.PlayerAdded:Connect(function(plr)
	while wait(1) do
		script.Parent.Visible = true
		if plr:WaitForChild("Values"):WaitForChild("Daily").Value == 0 then
			plr:WaitForChild("Values"):WaitForChild("Daily").Value = 86400
			local chance = math.random(1, 20)
			if chance == 1 or chance == 2 or chance == 3 or chance == 4 or chance == 5 or chance == 6 or chance == 7 or chance == 8 or chance == 9 or chance == 10 or chance == 11 or chance == 12 then
				script.Parent.Description.Text = "You have earned 1,000 coins for logging in today! Come back tomorrow for a chance to get a free skip!"
				game.ReplicatedStorage.DailyChances["1000Coins"]:FireServer()
			elseif chance == 13 or chance == 14 or chance == 15 or chance == 16 or chance == 17 then
				script.Parent.Description.Text = "You have earned 2,500 coins for logging in today! Come back tomorrow for a chance to get a free skip!"
				game.ReplicatedStorage.DailyChances["2500Coins"]:FireServer()
			else
				script.Parent.Description.Text = "You have earned a free skip for logging in today! Come back tomorrow for a chance to get another one!"
				game.ReplicatedStorage.FreeSkip:FireServer()
			end
		else
			script.Parent.Visible = false
		end
	end
end)

Help is appreciated! Also this is how I might make an idle game, but I think this is the first step.

1 Like

I would try to make a datastore for the timer, save it, and keep it counting down, even if the player is not online, but im not sure how to do that, that’s just an idea, I think there is another way.

2 Likes

Also don’t worry about the GUI and the remote events. They work but not the timer.

1 Like

When the player joins just create a number value and set its value to os.time. Then when they’re leaving save the value to the data store. When they click the button check if os.time() - player.leaderstats.Time.Value >= 86400. You can set their time value on the server back to os.time() once they collect the reward

2 Likes

Bad saying, doing so will make it so u get daily rewards 24h after u leave not claim reward, instead change the value when the reward is collected

2 Likes

I said create a value when they join, I’m not setting a new os.time when they’re leaving. As for collecting the reward, I omitted that as I was rather in a hurry

2 Likes

@MysteryX2076 solved it but i think you should wrap your get and set asyncs in a pcall. They could error at any given moment so a pcall gives you the ability to handle an error with care

1 Like

It works but it only counts when the player is in the game
btw i see no errors except the observational thing

1 Like

was it because that i used the DataStoreService the wrong way? i have a script that saves everything in the values folder inside of the script creating the leaderstats and all of that(same as leaderstats)

local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")
local Saver = DataStoreService:GetDataStore("SavePlrValues")

Players.PlayerAdded:Connect(function(player)
	local Data = nil
	local success, errormessage = pcall(function()
		Data = Saver:GetAsync(tostring(player.UserId))
		print("Successfully loaded "..tostring(player.Name)..tostring("'s data."))
	end)

	if success then
		if Data then
			for i, v in pairs(Data) do
				player:WaitForChild("Values"):WaitForChild(i).Value = v
			end
		end
	else
		error(errormessage)
	end
end)

local function Save(player)
	local SavedData = {}
	for _, v in pairs(player.Values:GetChildren()) do
		SavedData[v.Name] = v.Value
	end

	local success, errormessage = pcall(function()
		Saver:SetAsync(tostring(player.UserId), SavedData)
		print("Successfully got "..tostring(player.Name)..tostring("'s data."))
	end)
	if not success then
		error(errormessage)
	end
end

Players.PlayerRemoving:Connect(Save)

game:BindToClose(function()
	for _, v in pairs(Players:GetPlayers()) do
		Save(v)
	end
end)

Well you seem to be saving the right way (Unless the daily reward value isn’t in there). Please do look at my post above on how to implement the daily thing. Here’s how you would prob use it in the local script

--[[Client code]]--
local dailyrewardsbutton = script.Parent -- Script in button
local player = game.Players.LocalPlayer
local dailyevent = game.ReplicatedStorage.DailyEvent
dailyrewardsbutton.Activated:Connect(function()
   local success = dailyevent:InvokeServer()
   if success then dailyrewardsbutton.Text = 'Success!' end
end)
--[[Server code]]--
local dailyevent = game.ReplicatedStorage.DailyEvent
dailyevent.OnServerInvoke = function(plr)
   if os.time() - plr.Values.Daily.Value >= 86400 then
      local chance = math.random(1, 20)
			if chance == 1 or chance == 2 or chance == 3 or chance == 4 or chance == 5 or chance == 6 or chance == 7 or chance == 8 or chance == 9 or chance == 10 or chance == 11 or chance == 12 then
				script.Parent.Description.Text = "You have earned 1,000 coins for logging in today! Come back tomorrow for a chance to get a free skip!"
				game.ReplicatedStorage.DailyChances["1000Coins"]:FireServer()
			elseif chance == 13 or chance == 14 or chance == 15 or chance == 16 or chance == 17 then
				script.Parent.Description.Text = "You have earned 2,500 coins for logging in today! Come back tomorrow for a chance to get a free skip!"
				game.ReplicatedStorage.DailyChances["2500Coins"]:FireServer()
			else
				script.Parent.Description.Text = "You have earned a free skip for logging in today! Come back tomorrow for a chance to get another one!"
				game.ReplicatedStorage.FreeSkip:FireServer()
			end
          return true
   else
      return false
   end
end

Just remove the events in the server code, you can directly set the money and stuff

You don’t see errors because the datastores are working fine until they don’t.

Look at this even roblox uses pcalls when getting/settings data!
Read this: Data Stores | Documentation - Roblox Creator Hub

I don’t have a script for the “DailyEvent” remote event, what do i do on it?

I only have the ones to award the player with the daily gift
Btw I might need to answer later because its getting late for me. Help = appreciated even when I’m away

I just noticed but what you’re doing by giving the client the power to decide how much coins they can have is extremely dangerous. This will allow exploiters to rapid-fire the daily chances event until they basically have inf money.

Make the remote function in replicated storage and follow my code

For the third time, make sure you create a daily number value when the player joins and assign the daily value the saved value when they left previously

Is there any way to fix it? btw now i understand what he’s saying(post 10)

also i got an error:
OnServerInvoke is not a valid member of RemoteEvent "ReplicatedStorage.DailyEvent"

It got fixed when I changed it to RemoteFunction

I did a different way. It appears to work now.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.