"Be online for 10 hours" task broken

Hey,

I would like to create a task that tracks your online presence, and you need to be online for 10 hours to complete it. I’m using the “hr2” value to determine which task you should perform. The task “Be online for 10 hours” is assigned when hr2.Value = 5. ht2 represents the total time you’ve been online, and the task is considered complete when ht2.Value >= 36,000 seconds (= 10 hours).

The issue I’m encountering is that sometimes the timer behaves unexpectedly. It’s a bit challenging to describe, so I’ve included a video below to demonstrate the problem.

Here is the code:



local function updateHours(value)
	local seconds = value
	local hours = seconds / 3600
	local formattedHours = string.format("%.1f h", hours) 
	return formattedHours
end


       ht2.Changed:Connect(function()
        ...
	elseif hr2.Value == 5 then
		if ht2.Value >= 36000 then
			coin.Value = coin.Value + 1000000000  -- don't care about this line
			coe:FireServer(coin.Value) -- don't care about this line
			hteht = 2  -- don't care about this line
			hr2.Value = 0
			ht2.Value = 0
			wait()
			hdone = 'Be online for 10 hours' -- don't care about this line
			done() -- don't care about this line
			vaikeus = 3 -- don't care about this line
			t2.task2.Text = "x"
		else
			if vaikeus == 3 then
				hourht2 = updateHours(ht2.Value)
				t2.task2.Text = hourht2 .. "/ 10h"
			end
			wait(1)
			ht2.Value += 1
			h2:FireServer(ht2.Value)
		end
         ...
    end)

please tell me the solution. I tried to fix it but still the same problem.

2 Likes

You mean, in game? or online (roblox.com)

I mean in game. So every time when you are playing the game, it counts and when you’re not, it doesn’t count

1 Like

Do you mean staying in the game for 10 hours? or count the total time. If it is the total time, you will need the datastore.

1 Like

it could be because you are doing coe:FireServer before assigning the time value, i think :FireServer waits until the server has received the message before moving onto the next line of code.

the total time. I have the datastore already. It locates in the ServerScriptService and this taskscript locates in the StarterGui

Here is the saving system:

local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local DataStoreService = game:GetService("DataStoreService")

local database = DataStoreService:GetDataStore("(Mydatastore)")
local sessionData = {}

function PlayerAdded(player)
...

local tasks = Instance.new(Folder)

tasks.Name = "tasks"

	local hraha = Instance.new("IntValue", tasks)
	hraha.Name = "hraha"
	local hraha2 = Instance.new("IntValue", tasks)
	hraha2.Name = "hraha2"
	local hraha3 = Instance.new("IntValue", tasks)
	hraha3.Name = "hraha3"

	local ht1 = Instance.new("IntValue", tasks)
	ht1.Name = "ht1"
	local ht2 = Instance.new("IntValue", tasks)
	ht2.Name = "ht2"
	local ht3 = Instance.new("IntValue", tasks)
	ht3.Name = "ht3"

	local success = nil
	local playerData = nil
	local attempt = 1

	repeat
		success, playerData = pcall(function()
			print("toimii")
			return database:GetAsync(player.UserId)
		end)


		attempt += 1
		if not success then
			warn(playerData)
			loaded.Value = 1
			task.wait(3)
		end
	until success or attempt == 5
	if success then
		print("Connected to database")
		if not playerData then
			print("Assigning default data")
			playerData = {
                                ...
				["r2aha1"] = 0,
				["r3aha1"] = 0,
				["hr1aha"] = 0,
				["hr2aha"] = 0,
				["hr3aha"] = 0,
				["raha2"] = 0,
				["raha3"] = 0,
				["raha4"] = 0,
				["et22"] = 0,
				["et33"] = 0,
				["mt1"] = 0,
				["mt2"] = 0,
				["mt3"] = 0,
				["ht1"] = 0,
				["ht2"] = 0,
				["ht3"] = 0,
				["it1"] = 0,
				["it2"] = 0,
				["it3"] = 0,
                                ...
			}
		end
		sessionData[player.UserId] = playerData
		
	else
		warn("Unable to get data for", player.UserId)
		player:Kick("Unable to load your data. Try again later")
	end

	hraha.Value = sessionData[player.UserId]["hr1aha"]
	hraha2.Value = sessionData[player.UserId]["hr2aha"]
	hraha3.Value = sessionData[player.UserId]["hr3aha"]

	ht1.Value = sessionData[player.UserId]["ht1"]
	ht2.Value = sessionData[player.UserId]["ht2"]
	ht3.Value = sessionData[player.UserId]["ht3"]

	hraha.Changed:Connect(function()
		sessionData[player.UserId].hr1aha = hraha.Value
		print("hraha1")
		sessionData[player.UserId].ht1 = ht1.Value
		print("ht1")
	end)
	
	hraha2.Changed:Connect(function()
		sessionData[player.UserId].hr2aha = hraha2.Value
		print("hraha2")
		sessionData[player.UserId].ht2 = ht2.Value
		print("ht2")
	end)
	
	hraha3.Changed:Connect(function()
		sessionData[player.UserId].hr3aha = hraha3.Value
		print("hraha3")
		sessionData[player.UserId].ht3 = ht3.Value
		print("ht3")
	end)

	tasks.Parent = player

end

Players.PlayerAdded:Connect(PlayerAdded)

function PlayerLeaving(player)
	if sessionData[player.UserId] then
		local success = nil
		local errorMsg = nil
		local attempt = 1

		repeat
			success, errorMsg = pcall(function()
				
				database:SetAsync(player.UserId, sessionData[player.UserId])
			end)
			attempt += 1
			if not success then
				warn(errorMsg)
				task.wait(3)
			end
		until success or attempt == 5

		if success then
			print("Data saved for", player.Name)
		else
			warn("Unable to save for", player.Name)
		end

	end

end

Players.PlayerRemoving:Connect(PlayerLeaving)

function ServerShutdown()
	if RunService:IsStudio() then
		
		return 
	end
	print("Server shutting down...")
	for _, player in pairs(Players:GetPlayers()) do
		task.spawn(function()
			PlayerLeaving(player)
		end)
	end
end
game:BindToClose(ServerShutdown)


(hraha2 is the same as hr2)

there are also the other tasks values but only the hr2 and ht2 is the value that we want to fix.

Also there is another server script that tells the datastore script that ht2 and hr2 value has changed.

Here it is:

game.ReplicatedStorage.tasks.hr.OnServerEvent:Connect(function(player, newCoins)
	local saunat = player:WaitForChild("tasks")
	local sauna2 = saunat:FindFirstChild("hraha")
	wait(.5)
	sauna2.Value = newCoins
end)

game.ReplicatedStorage.tasks.hr2.OnServerEvent:Connect(function(player, newCoins)
	local saunat = player:WaitForChild("tasks")
	local sauna2 = saunat:FindFirstChild("hraha2")
	wait(.5)
	sauna2.Value = newCoins
end)

game.ReplicatedStorage.tasks.hr3.OnServerEvent:Connect(function(player, newCoins)
	local saunat = player:WaitForChild("tasks")
	local sauna2 = saunat:FindFirstChild("hraha3")
	wait(.5)
	sauna2.Value = newCoins
end)

game.ReplicatedStorage.tasks.h1.OnServerEvent:Connect(function(player, newCoins)
	local saunat = player:WaitForChild("tasks")
	local sauna2 = saunat:FindFirstChild("ht1")
	wait(.5)
	sauna2.Value = newCoins
end)

game.ReplicatedStorage.tasks.h2.OnServerEvent:Connect(function(player, newCoins)
	local saunat = player:WaitForChild("tasks")
	local sauna2 = saunat:FindFirstChild("ht2")
	wait(.5)
	sauna2.Value = newCoins
end)

game.ReplicatedStorage.tasks.h3.OnServerEvent:Connect(function(player, newCoins)
	local saunat = player:WaitForChild("tasks")
	local sauna2 = saunat:FindFirstChild("ht3")
	wait(.5)
	sauna2.Value = newCoins
end)

Hopefully this helps…

That RemoteEvent should activate only when you have ht2.Value >= 36000. That RemoteEvent indicates that the “coins.Value” has changed and then stores it in the datastore. It shouldn’t be a problem because the same kind of RemoteEvents are used in the other tasks and those tasks works 100% correctly.

Just to clarify - are you expecting the ht2.Value to change more than once per second? It seems like it is updating much faster than that in your video.

1 Like

it has to get 1 value more every second. So when ht2 = 30, it means that you have played for 30 seconds. It shouldn’t behave as shown in the video. That’s the problem I’m encountering. It usually works correctly, meaning it consistently increments the value by one every second, but at some point, it breaks, and the same thing as in the video happens.

After you Connect to ht2.Changed do you just change the value inside it’s own block?

That would create an infinite cycle of chained Change events and looking at existing conditional logic, it’d definitely have extremely strange behaviour.

Also why are the variables named in such an obfuscated way :sob:

1 Like

After you Connect to ht2.Changed do you just change the value inside it’s own block?

Changing the value inside the event handler shouldn’t matter in and of itself - it’ll just cause another changed event to fire, which is what they want. He’s also got the wait(1) before changing the value to throttle the changes to 1 sec.

It’s a bit of a strange implementation - I wouldn’t do it this way but in theory, it should work fine.

@FGJepperigamer my guess is that something outside of the code you’ve shown is causing the problem e.g. what code is being called to setup the changed event? Are you confident it is only called once?

1 Like

What I’ll often do when things that should work don’t “seem to be working” is I’ll open a fresh, blank baseplate and reproduce what I think should work and see if it does.

If working then the problem is somewhere outside the code contained in the baseplate.
If not working, the problem is in the code in the baseplate.

Here is a the simplest version of what you’re doing - an IntValue is added to the workspace on startup and incremented in its own change handler every second. When I run it, it works as expected.

@FGJepperigamer if you run this and watch the value behave correctly past the point where yours is breaking then the issue is not in the change handling.

recursive-value-change.rbxl (52.9 KB)

sorry about that… I don’t like long variable names so I use shorter. ht2 is HardTask2 and hr2 is HardRandom2.

But… is there better way to do that system? It can be possible what you said. I mean this: “That would create an infinite cycle of chained Change events and looking at existing conditional logic, it’d definitely have extremely strange behaviour.” And yes, I have ht2.Changed and when the value changes, it checks that if you have 36000 and if not, it increments the value by one. If it is over 36000, it gives the reward.

would you like to see the entire code? It’s over 2600 line code… and that’s why I didn’t post the entire code. If you want to see entire code, I will paste the code into dpaste.org and then share the link because it’s too long and messy.

is there better way to do that system?

I typically use the RunService.Heartbeat like this:

-- update Player "Time Played"
local LastTimePlayedUpdate = 0
local WRITE_TIME_PLAYED_SEC = 30

RunService.Heartbeat:Connect(function(step)
	LastTimePlayedUpdate += step
	
	if LastTimePlayedUpdate > WRITE_TIME_PLAYED_SEC then
		LastTimePlayedUpdate = 0
		for i, p in Players:GetPlayers() do
			UserSettingsStore.saveUserSetting(p, Consts.ATT_TIME_PLAYED_MIN, WRITE_TIME_PLAYED_SEC/60, true, true)
		end
	end
end)

That’s just an example. You’d likely have to refactor the way your handling things to make this work.

What approach I would take is,

  • When player joins, check if they have a datastore entry and load its value into a table.

  • Have a RunService runner increment entire table using the server uptime argument.

  • When player leaves, save the entry and clean up.

A lot simpler of an approach.

Agreed :slight_smile: My example code shows the partial implementation of this.

It’s really hard to read :sweat_smile: sorry

Edit: nvm, I was talking about OP’s code

Ok, thanks all. I’ll try those tomorrow and tell the results. If it’s not working or/and you want to see the entire code, I’ll tell and/or show.

1 Like

Happy to take a look at the larger solution but agree with @Anurag_UdayS that those variable names might make it hard to grok :slight_smile:

1 Like