Questions about nil values in table

Hello! I am making a table that stores player instances. But if a player left, will that player instance in the table become nil? And if yes, can I remove that with table.remove()?

You need to first find the player that was removed with table.find, here is a code example:

local myCoolPlayerTable = {}

game.Players.PlayerAdded:Connect(function(player)
	table.insert(myCoolPlayerTable, player)
	print(myCoolPlayerTable) -- prints all players who joined
end)

game.Players.PlayerRemoving:Connect(function(player)
	local PlayerPos = table.find(myCoolPlayerTable, player)
	table.remove(myCoolPlayerTable, PlayerPos)
	print(myCoolPlayerTable) -- prints all players except the one who left (might be an empty table when there was only 1 player in the game)
end)

Adding on to this, if you don’t wish to use table.remove for whatever reason, you can also set the index to nil and it should do the same thing table.remove does.

If it’s a local script the script will become by default nil meaning the table wouldn’t exist for that certain client once he’s left the game but doing it on the server side or by script it would only set to nil once the game has shutdown.

So if you have the table on the server side you’ll have to set it to nil manually.

Why even make a seperate table? Why not just use game.Players:GetPlayers()?

You’d have to constantly update it

Why bother with all these elaborate procedures with the table library? Just do this:

local playerService = game:GetService("Players")
local playerDataTable = {}

playerService.PlayerAdded:Connect(function(player)
	local key = player.UserId
	playerDataTable[key] = player
end)

playerService.PlayerRemoving:Connect(function(player)
	local key = player.UserId
	playerDataTable[key] = nil
end)

That’s all there is to it. You use the player’s UserId as the key into the table. If something weird happens, you can audit the table by checking each entry and see if they are in the list provided by Players:GetChildren(). Alternatively, you can use the player instance itself as the key as well. Not sure what you are using it for though so I can’t recommend which way you want to go with this.

No? When ever you want to use the table just call that function.

Or

local plrtable  = {}
local function UpdatePlrTable()
   table.clear(plrtable)
   for _,plr in pairs(game.Players:GetPlayers())
      table.insert(plrtable,plr)
   end
end

game.Players.PlayerAdded:Connect(UpdatePlrTable)
game.Players.PlayerRemoving:Connect(UpdatePlrTable)

But I am local scripting a spectate system so game.Players.PlayerAdded and Removing is kinda confusing for me
Here’s the script:

local button = script.Parent
local ScreenGui = button.Parent
local plr = game.Players.LocalPlayer
local ShopE = ScreenGui.SpecE
local number = 1
local speclabel = ScreenGui.Spectate
local cam = game.Workspace.CurrentCamera
local function getplayers()
	local e = {}
	for i,plr in pairs(game.Teams.Player:GetPlayers()) do
		if plr.Character and plr.Character:FindFirstChild("Humanoid") and plr.Character:FindFirstChild("Humanoid").Health ~= 0 then
			table.insert(e,plr)
		end
	end
	return e
end
local currentplayers
local ts = game:GetService("TweenService")
local tsinfo = TweenInfo.new(
	0.4,
	Enum.EasingStyle.Linear,
	Enum.EasingDirection.Out,
	0,
	false,
	0
)
button.MouseButton1Click:Connect(function()
	button.Click:Play()
	if speclabel.Visible then
		speclabel.Visible = false
		cam.CameraSubject = plr.Character.Humanoid
		else
		if plr.Team == game.Teams.Lobby and game.Workspace.CanSpectateAndBuy.Value then
			local plrss = getplayers()
			if #plrss > 0 then
				currentplayers = plrss
				speclabel.Visible = true
				number = 1
				cam.CameraSubject = plrss[number].Character.Humanoid
				speclabel.Text = plrss[number].Name
			end
		end
	end 
end)
game.Workspace.CanSpectateAndBuy.Changed:Connect(function()
	if game.Workspace.CanSpectateAndBuy.Value == false then
		speclabel.Visible = false
		cam.CameraSubject = plr.Character.Humanoid
	end
end)
speclabel.Right.MouseButton1Click:Connect(function()
	local testnumber = number+1
	local spectateplr
	repeat
	if testnumber > #currentplayers then
		testnumber = 1
	end
	if currentplayers[testnumber] and currentplayers[testnumber].Character:FindFirstChild("Humanoid") and currentplayers[testnumber].Character.Humanoid.Health ~= 0 then
		else
		table.remove(currentplayers,currentplayers[testnumber])
		testnumber+=1
	end
	until (currentplayers[testnumber] and currentplayers[testnumber].Character:FindFirstChild("Humanoid") and currentplayers[testnumber].Character.Humanoid.Health ~= 0) or #currentplayers == 0
	if #currentplayers ~= 0 then
		number = testnumber
		cam.CameraSubject = currentplayers[number].Character.Humanoid
		speclabel.Text = currentplayers[number].Name
	end
end)
speclabel.Left.MouseButton1Click:Connect(function()
	local testnumber = number-1
	local spectateplr
	repeat
		if testnumber <1 then
			testnumber = #currentplayers
		end
		if currentplayers[testnumber] and currentplayers[testnumber].Character:FindFirstChild("Humanoid") and currentplayers[testnumber].Character.Humanoid.Health ~= 0 then
		else
			table.remove(currentplayers,currentplayers[testnumber])
			testnumber-=1
		end
	until (currentplayers[testnumber] and currentplayers[testnumber].Character:FindFirstChild("Humanoid") and currentplayers[testnumber].Character.Humanoid.Health ~= 0) or #currentplayers == 0
	if #currentplayers ~= 0 then
		number = testnumber
		cam.CameraSubject = currentplayers[number].Character.Humanoid
		speclabel.Text = currentplayers[number].Name
	end
end)
button.MouseEnter:Connect(function()
	ts:Create(ShopE,tsinfo,{BackgroundTransparency = 0.5}):Play()
end)
button.MouseLeave:Connect(function()
	ts:Create(ShopE,tsinfo,{BackgroundTransparency = 1}):Play()
end)

Well, if you’re going to go through all that, then just do this:

local plrtable  = {}
local function UpdatePlrTable()
	plrtable = game.Players:GetPlayers()
end

game.Players.PlayerAdded:Connect(UpdatePlrTable)
game.Players.PlayerRemoving:Connect(UpdatePlrTable)

game.Players:GetPlayers() returns a table, so just use that. No need to copy it in a for loop into a new table.

Yep I did mention that but why go through the hassle of doing that when you can simply whenever you call plrtable you replace it with game.Players:GetPlayers()

Pardon me but… what? Care to explain a bit more?

In any case, you’re still going to have to burn CPU time on the server for it. The solution that I posted works well. I see nothing wrong with it because it’s event driven, as yours is. I believe that mine is simpler to use and understand. Plus, it uses the player’s UserId as the key which Rolbox guarantees is unique to each player unlike the name.

He’s doing a spectate. That’s on the client.

As players are added and removed from the server, the table of players get’s updated. That’s all that Players.PlayerAdded and Players.PlayerRemoving does. There’s a community tutorial on this that’s pretty good. I’ve used it myself when making a spectate.

local playerService = game:GetService("Players")
local localPlayer = playerService.LocalPlayer
local playerDataTable = {}

-- Add players to the list when the join the server.
playerService.PlayerAdded:Connect(function(player)
	playerDataTable[player.UserId] = player
end)

-- Remove players from the list when they leave the server.
playerService.PlayerRemoving:Connect(function(player)
	playerDataTable[player.UserId] = nil
end)

-- Initial filling of the table.
for _, player in ipairs(playerService:GetPlayers()) do
	if player ~= localPlayer then
		playerDataTable[player.UserId] = player
	end
end)

That’s the basics as I remember them. You fill the table initially, then just add or remove players when the arrive or leave the server. It still needs the events to handle the spectate toggle, next player, and previous player GUI buttons, but that’s the gist of it.

1 Like

So, i made a nil value called currentplayers and a number var = 1. And inside the spectate button click event, I set currentplayers to a table that contains players with choosen team and set number to 1 and make the players cam’scamera subject to currentplayers[number].Character.Humanoid.
In the “right” button click event. I checked if currentplayers[number+1] isn’t nil(I guess if that player left that value will become nil). Same with left button but number-1
I can’t test cuz my potato pc will explode if I open 2 boblox apps. So can you tell me is my thought about when a player left, all that player instances in every table will become nil right?

I am not sure if a player goes nil or not when left. You can test that out with a friend perhaps? But if not then you can see if game.Players:FindFirstChild(currentplayers[number+1]) ~= nil

My guess is no. The reasoning is that the Player instance has an active reference to it in the table. When the player leaves, Player.Parent is set to nil, but it’s not removed from memory or garbage collected because of the active table reference. When the table entry gets set to nil, then the Player instance goes away.

@ImNotInfected13 If you code it that way and explicitly set the table entry representing the player to nil, you will not go wrong. It’s considered good practice to clean things up when you’re done using them.

Nvm I found a way to check. Just check if the parent is nil or not

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