Creating a Daily Reward system based off client's local time

I’m trying to create a daily reward system that only rewards a player exactly once per day. This system would allow a player to collect their daily reward late a night and be able to do so once again early in the morning with a reset at midnight local time. I attempted to write this using a combination of DateTime and sending the clients local time to the server but I just couldn’t wrap my head around the logic.

2 Likes

Couldn’t you save the date when the reward was collected then when next attempt to collect daily reward check if its greater then the day/month/year last collected.

edit: using something like

os.date(“%c”)

Yes, you can send the client’s os.date() to the server, more specifically the day and year, and check if the player has already claimed the reward for the day with a datastore and a RemoteFunction (or RemoteEvent)

LocalScript:

-- Reward is a RemoteFunction in ReplicatedStorage
local canReward = game.ReplicatedStorage:WaitForChild("Reward"):InvokeServer(os.date("%j%Y")) -- Send the day and year to the server

if canReward then
	print("I can collect the daily reward")
else
	print("I already claimed today's reward")
end

Server script:

local datastore = game:GetService("DataStoreService"):GetDataStore("Reward")

game.ReplicatedStorage:WaitForChild("Reward").OnServerInvoke = function(player, date)
	if datastore:GetAsync(player.UserId) ~= date then -- Has this player already claimed the reward today
		datastore:UpdateAsync(player.UserId, function(old)
			local new = old or 0
			new = date
			return new
		end)
		--reward stuff
		return true
	elseif datastore:GetAsync(player.UserId) == nil then -- If there is no date saved for reward collection
		datastore:SetAsync(player.UserId, date)
		--reward stuff
		return true
	else
		return false
	end
end
2 Likes

It shouldn’t be based on the client’s local time, it should be based on the server getting UTC time (w/ os.time()) when the client tells it to.

The client is not to be trusted.

Then, when the client is added, or when the server runs a core logic loop, get the last reward’s os.time() – current os.time().

@mymommakesbathbombs4’s example works with this idea, except that the client doesn’t send its time, the server just checks its own time (as it is always in UTC).

5 Likes

I’m mostly hung up on the logic that prevents exploiters from time jumping to collect rewards. I think the maximum time jump threshold should be one day in the future.

1 Like

You can use the clients time as long as the server verifies that the time is not too far into the future or past and other measures to prevent time jumping for extra daily rewards.

1 Like

I figured it out. Using the clients year and day was the best route to go. Thanks

1 Like

You could, but I think the server time is easier, as you do not have to calculate difference from real time or send a message back to the client when they may be ‘cheating’.

The time needs to go to your server anyway, so doing the other method is more of an inconvenience.

You need the client’s time in order to reset their daily rewards at midnight. Midnight is at a different for every player depending on their time zone.

1 Like

Okay, I didn’t know that was what you were looking for.

A lot of games simply wait 24 hours since the last claim.

I am on the same page now.