How do I make this progress bar update when someone joins the game (Im a beginner)

Hi, so I just got into scripting,

I’m currently having trouble with creating a progress bar for my Obby, I made the functionality of it updating whenever I move onto a new stage work… But I’m having trouble with Updating the progress bar whenever someone joins the game, so that it feels more polished…

One of the things I’ve tried is doing a remote event that connected to the local script from my leaderstats script, then trying to match the username of the player that just joined with the local player name, and if it matches then it will update the bar… But for some reason I couldn’t get the player’s Name from “Players.PlayerAdded:Connect(function(player)”…

I’m out of ideas, if someone could help me it would be wonderful!
Here is my current code, this is a local script:

function updateProgressBar(number)
	script.Parent.MainFrame.ProgressFrame.Size = UDim2.fromScale(number, 1)
end


local Achievement = 41 
local leaderstats = game.Players.LocalPlayer:WaitForChild("leaderstats")
local stageValue = leaderstats:WaitForChild("Stage") 
local stageText = script.Parent.MainFrame.TextLabel


stageValue:GetPropertyChangedSignal('Value'):Connect(function()
	updateProgressBar((stageValue.Value) / Achievement)
	local stagePercentage = (stageValue.Value) / Achievement * 100
	stageText.Text = math.round(stagePercentage) .. "%"
end)


And here is my leaderstats script, I copied it from YT but it does have a data store system that does save each player’s Stage.

local checkpoints = workspace:WaitForChild("Checkpoints")
local localPlayerJoinedEvent = game.ReplicatedStorage:WaitForChild("LocalPlayerJoined")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")

local DatastoreService = game:GetService("DataStoreService")
local Data = DatastoreService:GetDataStore("312374343")
local sessionData = {}

function PlayerAdded(player)
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"

	local stage = Instance.new("NumberValue")
	stage.Name = "Stage"
	stage.Parent = leaderstats

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

	repeat 
		success, playerData = pcall(function() -- here pcall or protected call is just repeat waiting until the data loads for the player
			return Data:GetAsync(player.UserId)
		end)

		attempt += 1
		if not success then 
			warn(playerData)
			task.wait(2)
		end

	until success or attempt == 5 -- it repeats it until it loads

	if success then --if it loads then make the table with their data inside
		print("Data loaded: "..player.Name)
		if not playerData then -- if they have no table then their a new player so we create the table 
			print("new player, giving default data")

			playerData = {
				["Stage"] = 1, --add all your values and stuff inside of the data

			}
		end

		sessionData[player.UserId] = playerData --set the data to a table with the players id and make to make a variable
	else
		warn("couldnt load data: "..player.Name)
		player:Kick("couldnt load your data, rejoin") --if the data couldnt load we kick them so their not just sitting there forever waiting
	end

	stage.Value = sessionData[player.UserId].Stage --here we get the numbervalue created above and get the value of it and set it to the value inside of the table

	stage:GetPropertyChangedSignal("Value"):Connect(function()
		sessionData[player.UserId].Stage = stage.Value --update the table value whenever the leaderstat value changes
	end)


	leaderstats.Parent = player


end

Players.PlayerAdded:Connect(function(player)
	PlayerAdded(player)
	
	player.CharacterAdded:Connect(function(char)
		local leaderstats = player:WaitForChild("leaderstats")
		local stage = leaderstats.Stage

		local hum = char:WaitForChild("Humanoid")
		task.wait()
		char:MoveTo(checkpoints[stage.Value].Position)

		hum.Touched:Connect(function(hit)
			if hit.Parent == checkpoints then
				if tonumber(hit.Name) == stage.Value + 1 then
					stage.Value += 1
				end
			end
		end)
	end)
end)

function PlayerLeaving(player)

	if sessionData[player.UserId] then
		local success = nil
		local errorMsg = nil
		local attempt = 1


		repeat
			success, errorMsg = pcall(function()
				Data:SetAsync(player.UserId, sessionData[player.UserId]) --here is the same as loading data just repeat waits until the data saves
			end)

			attempt += 1
			if not success then 
				warn(errorMsg)
				task.wait(2)
			end

		until success or attempt == 5

		if success then 
			print("Data saved: "..player.Name)
		else
			warn("Cant save: "..player.Name)
		end

	end

end

Players.PlayerRemoving:Connect(PlayerLeaving)

function ServerShutdown()
	if RunService:IsStudio() then
		return
	end

	for i, player in ipairs(Players:GetPlayers()) do
		task.spawn(function()
			PlayerLeaving(player)
		end)
	end
end
game:BindToClose(ServerShutdown)
1 Like

Do you have any Data stores, to save the value when a player leaves the game?

2 Likes

Have you tried player.Name instead of player? player gives you the actual player Instance and not the name.

1 Like

I’m somewhat confused, as you mentioned the progress bar after joining to load all the previous stages the player have already passed (prior to joining previously) requires datastores

Summary

to save their current stage number before the player leaves, then joining back to recover the data.

The PlayerAdded function inside the leaderstats script can be the issue with referencing the player’s name through the remote event. Is there an issue with the Client/Server replication?

1 Like

I copied a checkpoint script from youtube, and it did have a data store that stores the player’s Stage.Value

I used player.Name, then tried the script and it didn’t work, then I tried printing the player.Name and still came out as an error as well.

When I tried referencing the PlayerAdded’s name from my leaderstats and passing through a remote event into my local script, I couldn’t get the new player’s name for some reason.
Which means that I couldn’t match it with the local player’s name to update the progress bar

Also thanks for answering my thread! :slight_smile:

I’m assuming you’re trying to connect a remote event from server to client by doing something like :FireClient(player, stage.Value). Don’t know if it’s just me, but I don’t see that anywhere in the server script.

1 Like

Does the rest of your code work in the server script, once the player leaves then joins back the game by respawning on their original checkpoint?

1 Like

Yeah so for example if someone play and they leave on stage 15, the next time they play they will also be on stage 15.

And I also did something along the lines of:

Players.PlayerAdded:ConnectFunction(player)
PlayerJoinedEvent:FireClient(player)
end)

and then on the progress bar local script I think I did:

PlayerJoinedEvent.OnClientEvent:Connect(function(player)
local playerName = player.Name

if playerName == game.Players.LocalPlayer then

updates progress bar**

end)

But this didn’t work, there was an error with the player that I passed through the remote event and I couldn’t use the name. So I deleted it loll.
If you want me to replicate this problem then I am more than happy to! Thanks for helping me out!

Everything from the server script seemed fine to me for the most part. Do you remember what error showed up when you printed the player’s name? Try creating the same remote events but including the leaderstats.Stage.Value with player into one of the parameters when you fire the client.

1 Like

Here’s the problem. When you run :FireClient(player), the .OnClientEvent connection does not take player as a parameter.

:FireClient() takes player as an argument to know what player to send the event to.
.OnClientEvent ignores the player argument so what really happens is no data was passed over, the player argument is only there to specify what player takes the event.

What you could do is:

Players.PlayerAdded:Connect(function(player)
    PlayerJoinedEvent:FireClient(player)
end)
PlayerJoinedEvent.OnClientEvent:Once(function()
    --you don't need to compare usernames since this was already specified in the FireClient call
    --update progress bar
end)

If you want, here are some :FireClient() and .OnClientEvent connection examples:

exampleEvent:FireClient(examplePlayer, "HELLO", 112301)
exampleEvent.OnClientEvent:Connect(function(param1, param2)
    print(param1, param2) --> "HELLO 112301"
end)

exampleEvent2:FireClient(anotherPlayer)
exampleEvent2.OnClientEvent:Connect(function(param1, param2, param3, param4)
    print(param1, param2, param3, param4) --> "nil nil nil nil" (because no data was passed)
end)

exampleEvent3:FireClient("hello") --> errors because the first argument must be a player

Hiya, so I replicated the code like this

Server Script (the leaderstats and stagevalue portion), my event is called LocalPlayerJoined, and I put the FireClient() at the bottom of this script.:

local checkpoints = workspace:WaitForChild("Checkpoints")
local localPlayerJoinedEvent = game.ReplicatedStorage:WaitForChild("LocalPlayerJoined")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local DatastoreService = game:GetService("DataStoreService")
local Data = DatastoreService:GetDataStore("312374343")
local sessionData = {}

local LocalPlayerJoinedEvent = game.ReplicatedStorage.LocalPlayerJoined



function PlayerAdded(player)
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"


	local stage = Instance.new("NumberValue")
	stage.Name = "Stage"
	stage.Parent = leaderstats

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

	repeat 
		success, playerData = pcall(function() -- here pcall or protected call is just repeat waiting until the data loads for the player
			return Data:GetAsync(player.UserId)
		end)

		attempt += 1
		if not success then 
			warn(playerData)
			task.wait(2)
		end

	until success or attempt == 5 -- it repeats it until it loads

	if success then --if it loads then make the table with their data inside
		print("Data loaded: "..player.Name)
		if not playerData then -- if they have no table then their a new player so we create the table 
			print("new player, giving default data")

			playerData = {
				["Stage"] = 1, --add all your values and stuff inside of the data

			}
		end

		sessionData[player.UserId] = playerData --set the data to a table with the players id and make to make a variable
	else
		warn("couldnt load data: "..player.Name)
		player:Kick("couldnt load your data, rejoin") --if the data couldnt load we kick them so their not just sitting there forever waiting
	end

	stage.Value = sessionData[player.UserId].Stage --here we get the numbervalue created above and get the value of it and set it to the value inside of the table

	stage:GetPropertyChangedSignal("Value"):Connect(function()
		sessionData[player.UserId].Stage = stage.Value --update the table value whenever the leaderstat value changes
	end)

	leaderstats.Parent = player




	PlayerJoinedEvent:FireClient(player, stage.Value)
end

And then for the Local Script of the progress bar:


local LocalPlayerJoinedEvent = game.ReplicatedStorage:WaitForChild("LocalPlayerJoined")

local Achievement = 41 
local leaderstats = game.Players.LocalPlayer:WaitForChild("leaderstats")
local stageValue = leaderstats:WaitForChild("Stage") 
local stageText = script.Parent.MainFrame.TextLabel

function updateProgressBar(number)
	script.Parent.MainFrame.ProgressFrame.Size = UDim2.fromScale(number, 1)
end

stageValue:GetPropertyChangedSignal('Value'):Connect(function()
	updateProgressBar((stageValue.Value) / Achievement)
	local stagePercentage = (stageValue.Value) / Achievement * 100
	stageText.Text = math.round(stagePercentage) .. "%"
end)


playerJoinedEvent.OnClientEvent:Connect(function(player, stageNum)
	if player.Name == game.Players.LocalPlayer.Name then
		
		updateProgressBar((stageNum) / Achievement)
		
	
	end
	
end)

So when I try to play the game, I immediately get the error "Players.dogpoopgamenocap.PlayerGui.ProgressBar.LocalScript:20: attempt to index nil with ‘OnClientEvent’ "

What did I do wrong?

You never defined the playerJoinedEvent variable, the script doesn’t know what it is.

Here as well, it doesn’t know what PlayerJoinedEvent is.

The function should be .OnClientEvent:Connect(function(stageNum) and the if statement below the function isn’t needed since both the playerAdded function and the :FireClient(player) has already provided the player by default

1 Like

You will have to rename your remote event name to playerJoinedEvent so that firing the event functions.

1 Like

Omg wow, it worked!!!
thank you so much!!!

Yeah, yw

I’ve never posted in devforum before, glad to help out!