How can I log and save time spent ingame properly?

I have this script that’ll keep track of the time you spent in-game. The script itself is working fine, but the time spent won’t save properly. All answers appreciated.

local datastore = game:GetService("DataStoreService"):GetDataStore("Time")
-- since the datastore isn't based on the player, only the keys are,
-- there's no reason to define the datastore instead the PlayerAdded event

local joinTimes = {} -- store a table of when players joined

game:GetService("Players").PlayerAdded:Connect(function(player)

	local leaderstats = Instance.new("Folder", player)
	-- folders are meant for storing stuff, no need to use an intvalue
	leaderstats.Name = "leaderstats"

	local playTime = Instance.new("IntValue", leaderstats)
	playTime.Name = "Time"

	local key = player.UserId
	local success, data = pcall(datastore.GetAsync, datastore, key)
	-- make sure you handle errors properly, otherwise your code will break

	if success then -- if it worked,
		playTime.Value = data or 0 -- set it to whatever was saved (or 0 if nothing was saved)
		joinTimes[player] = {data or 0, tick()} -- keep track of when the player joined and what they had saved
	else
		warn(data) -- put the error in the console as a warning
		-- ... you can do better error handling here too
	end
end)

game:GetService("Players").PlayerRemoving:Connect(function(player)
	-- when a player leaves,
	local key = player.UserId
	local success, error = pcall(datastore.IncrementAsync, datastore, math.floor((tick() - joinTimes[player][2])/ 60))
	-- add onto the saved data the amount of time between when they joined and now in minutes

	if not success then -- if it errors,
		warn("UpdateAsync failed for", player, ":", error)
		-- retry or whatever error handling you want
	end
end)

game:GetService("RunService").Heartbeat:Connect(function()
	-- every frame, for every player, update their time played
	local now = tick()
	for player, times in pairs(joinTimes) do
		local leaderstats = player:FindFirstChild("leaderstats")
		if leaderstats and leaderstats:FindFirstChild("Time") then
			leaderstats["Time"].Value = times[1] + math.floor((now - times[2]) / 60)
			-- set their time played to what was saved + how long it's been since they've joined (in min)
		end
	end
end)

I assume it’s because you’re not using datastores properly. You have to do something like this

local success, err = pcall(function()
	visits = visitsDataStore:IncrementAsync(playerKey, 1)
end)

whereas you’re doing

local success, error = pcall(datastore.IncrementAsync, datastore, math.floor((tick() - joinTimes[player][2])/ 60))

I’ve never seen someone do that with a datastore before, so that’s what stood out to me.
This is a helpful article for using datastores.

1 Like

Actually after tweaking the code, I was able to get it to save properly, thanks!
Final code:

local datastore = game:GetService("DataStoreService"):GetDataStore("Minutes")
-- since the datastore isn't based on the player, only the keys are,
-- there's no reason to define the datastore instead the PlayerAdded event

local joinTimes = {} -- store a table of when players joined

game:GetService("Players").PlayerAdded:Connect(function(player)

	local leaderstats = Instance.new("Folder", player)
	-- folders are meant for storing stuff, no need to use an intvalue
	leaderstats.Name = "leaderstats"

	local playTime = Instance.new("IntValue", leaderstats)
	playTime.Name = "Minutes"

	local key = player.UserId
	local success, data = pcall(datastore.GetAsync, datastore, key)
	-- make sure you handle errors properly, otherwise your code will break

	if success then -- if it worked,
		playTime.Value = data or 0 -- set it to whatever was saved (or 0 if nothing was saved)
		joinTimes[player] = {data or 0, tick()} -- keep track of when the player joined and what they had saved
	else
		warn(data) -- put the error in the console as a warning
		-- ... you can do better error handling here too
	end
end)

game:GetService("Players").PlayerRemoving:Connect(function(player)
	-- when a player leaves,
	local key = player.UserId
	--local success, error = pcall(datastore.IncrementAsync, datastore, math.floor((tick() - joinTimes[player][2])/ 60))
	
	local success, err = pcall(function()
		datastore:IncrementAsync(key, math.floor((tick() - joinTimes[player][2])/ 60) )
		print("successful")
	end)
	--add onto the saved data the amount of time between when they joined and now in minutes
end)

game:GetService("RunService").Heartbeat:Connect(function()
	-- every frame, for every player, update their time played
	local now = tick()
	for player, times in pairs(joinTimes) do
		local leaderstats = player:FindFirstChild("leaderstats")
		if leaderstats and leaderstats:FindFirstChild("Minutes") then
			leaderstats["Minutes"].Value = times[1] + math.floor((now - times[2]) / 60)
			-- set their time played to what was saved + how long it's been since they've joined (in min)
		end
	end
end)
4 Likes