So times has never been my strongest point in scripting, let me just get that out the way.
Me and my developers had this idea of logging days, hours, minutes and seconds played. It would be stored in a DataStore of course and every time the player joins, they could see how many days, hours, etc they have played of the game.
This would open up other possibilities for what I have in mind.
Does anyone know how I could do this? Like I said this isn’t my strongest point lol.
You could create a bool value which addeds +1 every second. Then you could divide it by 60 for minutes, another 60 for hours, etc. and save the value whenever the player leaves.
Keep a table and everytime a player joins put there name as key and the value = getasync or 0. Add a while loop every 1 second adding to that value then save it when they leave.
The best method(s) to log the player’s total play time is to:
Start from 0 seconds
When they join, start the timer like this:
game:GetService("Players").PlayerAdded:Connect(function(player)
local start = tick()
while player.Parent do
player.leaderstats.TimePlayed.Value = player.leaderstats.TimePlayed.Value + (tick() - start)
start = tick()
wait() -- can extend
end
end)
Connect the value changes from a LocalScript:
local player = game:GetService("Players").LocalPlayer
player.leaderstats:WaitForChild("TimePlayed").Changed:Connect(function()
-- change the UI based on time change
end)
yourUpdateUIButton.MouseButton1Click:Connect(function()
-- less rate than previous function
end)
Loading and saving can be problematic if used incorrectly.
@MaximussDev Your saving and loading does not work well if the data stores decided to fail on the players
To fix this, add pcall with some error catching.
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local canSave = true
local secondsDS = DataStoreService:GetDataStore("SecondsDataStore")
Players.PlayerAdded:Connect(function(player)
local stats = Instance.new("Folder") -- do not use second parameter for good reasons from PSA
stats.Name = "Statistics"
local timePlayed = Instance.new("IntValue", stats) -- exception here, "NumberValue" is alternative
timePlayed.Name = "TimePlayed"
local success, data = pcall(secondsDS.GetAsync, secondsDS, player.UserId)
if success then
timePlayed.Value = data or 0
else
canSave = false
end
stats.Parent = player
end)
Players.PlayerRemoving:Connect(function(player)
if not canSave then
return
end
local stats = player.Statistics
local timePlayed = stats.TimePlayed
-- no time to break it down with UpdateAsync
local success, errorMessage = pcall(secondsDS.SetAsync, timePlayed.Value, player.UserId)
end)
The saving system above is not flawless, it can accidentally restart player’s data by mistake.
This is not the best system AFAIK.
I believe you can create the UI through server to the player, but I’m not certain yet.
It is clearly the thing about data store failure. It does not protect well against them, thus sometimes causing the player stat to reset to 0 due to failure of GetAsync. Error such as data not saving when SetAsync.
That’s why you have pcall and I think I got the SetAsync wrong.
local success, errorMessage = pcall(function()
secondsDS:SetAsync(player.UserId, timePlayed.Value)
end)
Errors like that are very obvious, and you can search for the mistake easily. The most common reasons that this happen are that leaderstats haven’t been loaded to the player or your script stops somewhere.
Some steps to solution is to check with print funtion if your leaderstats script actually works. Use WaitForChild function to allow your leaderstats folder to load first before the rest of the script runs.
You can keep a table in ServerScriptService that has a all the players by their index, and add them to the table. It wont be perfect since the server may lag, but here’s a sample of how your script would look
local DataStoreService = game:GetService('DataStoreService')
local DataStore = --blah blah blah
local PlayerTimes = {}
spawn(function()
while wait(1) do
for player,timeplayed in pairs (PlayersTimes) do
timeplayed = timeplayed +1
end
end)
game.Players.PlayerAdded:Connect(function(plr)
PlayerTimes[plr.Name] = 0
--OR you can load in their time and have it set instead of adding like im going to do in
--this code
end)
game.Players.PlayerRemoving:Connect(function(plr)
local Data = {}
--Add in all their other data blah blah blah
Data.Time = PlayerTimes[plr.Name]
PlayerTimes[plr.Name] = nil -- I think there's a better way to remove non-number indexes from tables but I dont know it
DataStore:UpdateAsync(theirkey,function(OldData)
local CurrentTime = OldData.Time
Data.Time = Data.Time + CurrentTime
return Data end)
end)
Got a little carried a way and made a little more than I planned to, but in short, this will keep all times the players play in a table constantly counting upwards
Well, first you have to use a unit of measurement. You could use seconds, minutes, or hours as the base unit. The larger you go, the less accurate it will be. Therefore, I recommend storing seconds.
What you would do is create a intValue, and every second increment it by 1. Save the value when a player leaves, and load it when a player joins (You could also autosave it, just make sure it gets saved and loaded).
Then, to convert that time into seconds, minutes, ect. You can do the following:
local value = intValue.Value --Get the player's value
local function getTime(value)
local day = 24*60*60 --Define how long each unit is relative to seconds
local hour = 60*60
local minute = 60
local second = 1
local days = math.floor(value/day) --Get max days that can fit
value = value - (day * days) --Remove the days from the value
local hours = math.floor(value/hour) --Get max hours
value = value - (hour * hours) --Subtract hours from the leftover amount
local minutes = math.floor(value/minute) --Get max minutes
value = value - (minute * minutes) --Subtract from leftover
local seconds = value
return days, hours, minutes, seconds
end
local days, hours, minutes, seconds = getTime(value)
--Do whatever you want with these values
print('Days: '..days..' Hours: '..hours..' Minutes: '..minutes..' Seconds: '..seconds)
This message was composed on a mobile device, sorry for any typos
No, I mean divide the number (which is in seconds) by 60, then divide it again (which is now in minutes), by 60.
So if you have 3600 seconds, if you divided it by 60, it would come up with 60 minutes, which would be the minutes, then it would divide by 60 again to get the hours.