Updating GUI on joined players

Hi,
So Im currently working on a DominationPointSystem.
Im managing all my UIs with FireAllClients Events. I have 2 Counter which are counting up. Now I noticed that when a new player joins, the counter is starting from 0, because of course I manage the UI on the Client and I want to keep it that way. Now I tried to get the new infos from older people in the game and give them to the new one that joins on the Server but I cant access the PlayerGui, which the Error, that my Frame “Points” is not existing.

Here’s the code:

OnPlr = {}
game.Players.PlayerAdded:Connect(function(plr)
	if #OnPlr ~= 0 then
		plr.PlayerGui:FindFirstChild("CaptureGUI").Points.T1.Text = OnPlr[1].PlayerGui:FindFirstChild("CaptureGUI").Points.T1.Text
		plr.PlayerGui:FindFirstChild("CaptureGUI").Points.T2.Text = OnPlr[1].PlayerGui:FindFirstChild("CaptureGUI").Points.T2.Text
	end
	
	table.insert(OnPlr, plr)
	print(OnPlr)
end)

game.Players.PlayerRemoving:Connect(function(plr)
	table.remove(OnPlr, table.find(OnPlr, plr))
	print(OnPlr)
end)

![image|181x243](upload://ul3ulfpMgHPd0imafFMs5Z6D5BU.png)


5 Likes

First things first:
This should also be a sort of server script so technically the thing you are trying to make will not work at all. Its not possible for it to work at all as well. Its best to see the second thing I mentioned.

local Players = game.Players
game.Players.PlayerAdded:Connect(function(plr)
	if #Players:GetPlayers() > 0 then
		plr.PlayerGui:FindFirstChild("CaptureGUI").Points.T1.Text = Players:GetPlayers()[1].PlayerGui:FindFirstChild("CaptureGUI").Points.T1.Text
		plr.PlayerGui:FindFirstChild("CaptureGUI").Points.T2.Text = Players:GetPlayers()[1].PlayerGui:FindFirstChild("CaptureGUI").Points.T2.Text
	end
end)

And second:
Its not a good practice to use a counter on the client. Instead add a numbervalue in say ReplicateStorage named counter and update it using a server script and then just display the value of the counter on every individual client.

3 Likes

I forgot to mention, that the code I posted here is in a Script in ServerScriptService and the Code you gave me is still putting out that error:

Also, I wonder when using :GetPlayers(), the new Player will be added to that so in your method the info of the newPlayer is given to the newPlayer… When Im right

3 Likes

Probably not.

I see but have you tried my second answer?

2 Likes

Disregarding the rather unusual approach you’ve taken to achieve what I’m not fully understanding yet, if you’re certain CaptureGUI will exist on all clients, simply yield your code until it does with a timeout.

Initially your use of FindFirstChild() here is completely redundant. FindFirstChild returns the queried Instance if it exists or returns nil. By doing this: plr.PlayerGui:FindFirstChild("CaptureGUI").Points.T1.Text, you’re invalidating and disregarding what the function might return, hence it will return no different result than directly referencing the raw path without using the function. If your FindFirstChild call returns nil, it’ll attempt to index nil with Points, and hence the error.

Additionally, you’re adding the plr Instance to the OnPlr array without checking if it already exists. Always helps to verify if what you’re attempting to add to the array already exists to minimize redundancy and unnecessary stacking of elements.

Similarly with your table.find() call, while understandable, the instance is bound to exist due to it’s guaranteed addition on PlayerAdded, for whatever reason if your table.find() call returned nil, your PlayerRemoving function would throw out for an error which could’ve been handled appropriately.

Without having you change too much of your code regardless of there being possibly better ways of handling this, below should be your tentative fix (keeping the above points in mind):

-- ServerScript

local Players = game:GetService("Players")

local OnPlr = {}

Players.PlayerAdded:Connect(function(plr)
	local CaptureGUI = plr:WaitForChild("PlayerGui"):WaitForChild("CaptureGUI", 3)
	
	if #OnPlr ~= 0 then
		local targetGUI = OnPlr[1]:WaitForChild("PlayerGui"):WaitForChild("CaptureGUI", 3)
		
		if CaptureGUI and targetGUI then
			CaptureGUI.Points.T1.Text = targetGUI.Points.T1.Text
			CaptureGUI.Points.T2.Text = targetGUI.Points.T2.Text
		end
	end
	
	if not table.find(OnPlr, plr) then
		table.insert(OnPlr, plr)
	end
	
	print(OnPlr)
end)

Players.PlayerRemoving:Connect(function(plr)
	local found = table.find(OnPlr, plr)
	
	if found then
		table.remove(OnPlr, found)
	end
	
	print(OnPlr)
end)
1 Like

Didnt tried it, because I would have to change tons of code, but I will keep it in mind for my next projects. Thank you for that

1 Like

So this is not printing an error, but testing it ingame with a friend, the text is not getting updated and is 0 and not a higher count which it was at my screen

The problem I see is how are you communicating two ways? A lot of people who code GUI’s and start out don’t understand how two way communication works with the server. Believe it or not, just like updating stats from a local script on a player, it will NEVER be saved to the server. You are only updating the server, you need to use a local script and a remote event fire when you would like to update the condition invokeserver to return the two way communication and then update the GUI on BOTH the SERVER and the CLIENT. I hope that helps you should look up some guides.

I see. In such situations, Tracing areas of your code through print-debugging can be helpful to check what bounds of your code are actually read when it’s ran.

From the sounds of it, it seems that either of CaptureGUI or targetGUI are returning nil, hence resulting in the condition not being true. Have you attempted to check if plr’s CaptureGUI and OnPlr[1]'s CaptureGUI both exist (on the server, in player’s PlayerGui) when the function is executed? Also, keeping in mind that when you first join, #OnPlr will be 0 until the array is further populated.
Where exactly is CaptureGUI located and how are you parenting it to PlayerGui?

Once again, I’m not quite understanding what exactly are you trying to achieve here, and the reason behind why you’ve picked this approach. But if you’d still like to stick with it, proceed with some baseline debugging to see what goes wrong and where.

1 Like

So I have a script to detect when the counter should rise and which then fires all clients to rise the counter +1. To that, it works completely fine but a problem occurs when a new player joins because the counter for the new player will start at 0 and I want now to get the current counter from an older player and transmit it to the new player so he starts at the right count

This is how my workspace is looking like:

image

The lines where the Text should be transmitted is running, since a print command is executed succesfull. The 2 OnPlr Prints are also working and printing the both player`s names

Why don’t you use one centralized variable or instance on the server that handles and stores the time and then dispatch that data to all clients as they join instead of treating clients like a server? This will help eliminate most of the discrepancy you have among times across clients and will maintain consistency and control over all client timers.

Well I know now that what I did was dumb as hell to do it like that but it would be hours of work again to change it so I tried looking for an alternative like I tried here

First of all, don’t worry, there’s nothing “dumb” about this. You’re still actively and logically trying to head towards a solution, just in a different, unnecessarily complex way.

May I ask why exactly would it take hours to change this approach? Can you show how are you counting the timers up in your LocalScript (assuming that’s where the counting is happening)? To me, this seems like a straightforward change which will help optimize your timers much more and eliminate lot of the unneeded complications you are/will run into.

Well so here is the part of the LOCAL SCRIPT in StarterGui (which you can see in my pic before) which rises the counter.



local Frame = script.Parent.Objects
local Points = script.Parent.Points
local Win = script.Parent.WinFrame

newC = coroutine.create(function() --Start of the coroutine
	while wait(1) do --While loop to stay the counter alive
		

-- Checks if the Counter reached its limit
		if tonumber(Points.T2.Text) >= 5000 then
			Win.T1.BackgroundColor = BrickColor.Red()
			GameOver("Red") --Function for the Win Event
		elseif tonumber(Points.T1.Text) >= 5000 then
			Win.T1.BackgroundColor = BrickColor.Blue()
			GameOver("Blue") --Function for the Win Event
		end
		
		
-- Checks which of the 2 Counters should rise, depending on the BrickColors of the 4 domination Zones (The color is changed in the same script here, but not included in this extract)
		if Frame.C1.BackgroundColor == BrickColor.Red() then
			Points.T2.Text += 1
		elseif Frame.C1.BackgroundColor == BrickColor.Blue() then
			Points.T1.Text += 1
		end
		if Frame.C2.BackgroundColor == BrickColor.Red() then
			Points.T2.Text += 1
		elseif Frame.C2.BackgroundColor == BrickColor.Blue() then
			Points.T1.Text += 1
		end
		
		if Frame.C3.BackgroundColor == BrickColor.Red() then
			Points.T2.Text += 1
		elseif Frame.C3.BackgroundColor == BrickColor.Blue() then
			Points.T1.Text += 1
		end
		
		if Frame.C4.BackgroundColor == BrickColor.Red() then
			Points.T2.Text += 1
		elseif Frame.C4.BackgroundColor == BrickColor.Blue() then
			Points.T1.Text += 1
		end
		
	end
end)

coroutine.resume(newC) --Firing Coroutine

Ah that makes so much more sense, of course your not replicating the data on the server to the client GUI. You need to make a variable (not a counter) that holds the counter amount on the server. Then when you fire that to all the clients you need to update the gui accordingly. If I were to write something like this it would go as follows:

--SERVER SCRIPT--
local players = game:GetService("Players")
local repStorage = game:GetService("ReplicatedStorage")
local timerRemote = repStorage:WaitForChild("TimerRemote")
local timer = 20 --change to number of seconds

function startTimer()
	for i = timer, 0, -1 do
		local playerList = players:GetPlayers() 
		for _, player in pairs(playerList) do
			timerRemote:FireClient(player, i)
		end
		task.wait(1)
	end
end

task.wait(5)
startTimer()

--LOCAL SCRIPT--
local players = game:GetService("Players")
local player = players.LocalPlayer or players.PlayerAdded:Wait()
local repStorage = game:GetService("ReplicatedStorage")
local timerRemote = repStorage:WaitForChild("TimerRemote")

timerRemote.OnClientEvent:Connect(function(i)
	local playerGui = player:WaitForChild("PlayerGui")
	local screenGui = playerGui:WaitForChild("ScreenGui")
	local frame = screenGui:WaitForChild("Frame")
	local textLabel = frame:WaitForChild("TextLabel")
	textLabel.Text = i.." seconds remaining!"
end)

original post of someone trying to update the timer to all players who join:

So I tested a bit around and I came to this result.
That works

--SERVER SCRIPT:


c1 = 0
c2 = 0

rep.PointsEvent.OnServerEvent:Connect(function(plr, T1, T2)
	c1 = T1
	c2 = T2
	print(c1)
end)


game.Players.PlayerAdded:Connect(function(plr)
	rep.CaptureEvent:FireClient(plr, 1, variables.C1.Value)
	rep.CaptureEvent:FireClient(plr, 2, variables.C2.Value)
	rep.CaptureEvent:FireClient(plr, 3, variables.C3.Value)
	rep.CaptureEvent:FireClient(plr, 4, variables.C4.Value)
	
	plr.PlayerGui:WaitForChild("CaptureGUI").Points.T1.Text = c1
	plr.PlayerGui:WaitForChild("CaptureGUI").Points.T2.Text = c2	
end)

rep.WinEvent.OnServerEvent:Connect(function(plr)
	c1 = 0
	c2 = 0
end)


--LOCAL SCRIPT:

local rep = game:GetService("ReplicatedStorage"):FindFirstChild("CaptureZones")


local Frame = script.Parent.Objects
local Points = script.Parent.Points
local Win = script.Parent.WinFrame

newC = coroutine.create(function()
	while wait(1) do
		
		if tonumber(Points.T2.Text) >= 5000 then
			Win.T1.BackgroundColor = BrickColor.Red()
			GameOver("Red")
		elseif tonumber(Points.T1.Text) >= 5000 then
			Win.T1.BackgroundColor = BrickColor.Blue()
			GameOver("Blue")
		end
		
		
		
		if Frame.C1.BackgroundColor == BrickColor.Red() then
			Points.T2.Text += 1
		elseif Frame.C1.BackgroundColor == BrickColor.Blue() then
			Points.T1.Text += 1
		end
		if Frame.C2.BackgroundColor == BrickColor.Red() then
			Points.T2.Text += 1
		elseif Frame.C2.BackgroundColor == BrickColor.Blue() then
			Points.T1.Text += 1
		end
		
		if Frame.C3.BackgroundColor == BrickColor.Red() then
			Points.T2.Text += 1
		elseif Frame.C3.BackgroundColor == BrickColor.Blue() then
			Points.T1.Text += 1
		end
		
		if Frame.C4.BackgroundColor == BrickColor.Red() then
			Points.T2.Text += 1
		elseif Frame.C4.BackgroundColor == BrickColor.Blue() then
			Points.T1.Text += 1
		end
		
		if tonumber(Points.T1.Text) >= 1 or tonumber(Points.T2.Text) >= 1 then
			rep.PointsEvent:FireServer(Points.T1.Text, Points.T2.Text)
		end
	end
end)

coroutine.resume(newC)

function GameOver(team)
	Win.T1.Text = "Team "..team.." has won the Game!"
	Frame.Visible = false
	Points.Visible = false
	Win.Visible = true
	
	Frame.C1.BackgroundColor = BrickColor.White()
	Frame.DC1.BackgroundColor = BrickColor.White()
	Frame.C2.BackgroundColor = BrickColor.White()
	Frame.DC2.BackgroundColor = BrickColor.White()
	Frame.C3.BackgroundColor = BrickColor.White()
	Frame.DC3.BackgroundColor = BrickColor.White()
	Frame.C4.BackgroundColor = BrickColor.White()
	Frame.DC4.BackgroundColor = BrickColor.White()
	
	
	rep.WinEvent:FireServer()
	wait(5)
	game.Players.LocalPlayer.Character.Humanoid.Health = 0
end

Tried that with a friend and it works.
Thanks for the help

1 Like

No problem! Just took some guiding to get you to the solution, everything takes time and effort. But I had the same exact problem and found a solution when people were saying the server GUI was updating and the player GUI wasn’t.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.