Table not passed properly outside of function (onserverevent)

Recently I have had a persistent problem which I am unable to solve. Simply put, any table updated under the tieBreaker.OnServerEvent:Connect(function(player, targetTile) is not being passed properly to other functions in the server script. I want to pass the playerTied table to the endIntermission function, but I am unsure how to fix the problem.

Server Script Code Snippet:

local connectionCount = 0 
local tietable = {}
local playerTied = {}
local newtable = {}

local function endIntermission(player)
	
	print(playerTied)
	if playerTied[player] then
		print(player .. " is tied, skipping transport.")
		tileDeleteEvent:FireAllClients(player)
	else
		print("not tied at all")
		tileDeleteEvent:FireAllClients(player)
		chooseTransport:FireAllClients(player)
	end
	
end

local function resetIntermissionState()
	connectionCount = 0
	tietable = {}
	playerTied = {}
end


local function onIntermissionStart(player)
	
	if not isIntermissionActive then
		isIntermissionActive = true -- Set the flag to true
	
		tieBreaker.OnServerEvent:Connect(function(player, targetTile)
			
			connectionCount += 1  -- Increment the count of players submitting tiles

			-- Add player and their target tile to tietable
			tietable[player.Name] = tietable[player.Name] or {}
			table.insert(tietable[player.Name], tostring(targetTile))
			
			-- Only run the logic when all players have submitted
			if connectionCount == #newmodule.storePlayers then
				print("All players have submitted their tiles!")
				
				print(tietable)

				-- Check for duplicates in the tietable
				local duplicates = duplicatefinder.find(tietable)
				
				if next(duplicates) ~= nil then
					duplicatefinder.resolve(duplicates)
					print("these are the duplicates")
					print(duplicates)
					
					for tile, players in pairs(duplicates) do
						for _, playerName in ipairs(players) do
							playerTied[playerName] = true
							print("Player tied: " .. playerName .. " on tile " .. tile)
						end
						print(playerTied)
						return playerTied
					end
					print("Updated playerTied table:", playerTied)	
				else 
					print("no duplicates found")
				end
			end
		end)
	end
end

local function roundFunction(player)

	decalchange.initialdecal()
	decalchange.createnew()
	newmodule.TransportonReady(player)
	intermission.start()

	while #newmodule.storePlayers > 0 do

		if not isIntermission then
			isIntermission = true -- Prevent events firing during intermission
			tileChangeEvent:FireAllClients(player)
			timerchange.timechange()
			-- Trigger intermission
			intermission.go()
			intermission.countround()
		
			onIntermissionStart(player) 
			endIntermission(player)
			resetIntermissionState()
		
			decalchange.createnew()
			decalchange.changeit()

			task.wait(2)
			isIntermission = false
		end
	end
end

Client Side Code:

chooseTransport.OnClientEvent:Connect(function()
	print("connection received")

	local player = players.LocalPlayer
	local character = player.Character or player.CharacterAdded:Wait()

	local choose = require(character.Choose)
	print("Attempting to transport player") 

	choose.transportplayer() -- Transport logic on the client side

	-- Debugging `choose.targetTile`
	if choose.targetTile then
		print("Target tile object:", choose.targetTile) 
		print("Target tile name:", choose.targetTile.Name)
		tieBreaker:FireServer(choose.targetTile.Name) -- Send targetTile name to the server
	else
		print("choose.targetTile is nil!") 
		warn("No target tile selected!") 
	end
end)

What is occurring is the playerTied table at the start of the endIntermission function is an empty table since the onintermissionstart is not passing it properly. How should I workaround this?

1 Like

Any suggestions at all would be very welcome!

1 Like

Is the output for the print statements nil? or are the statements not occurring at all?

if the print statements for the table are not printing a table, it could be because you are not using a table.Insert function to add players to the table.

I would like to see the output though as that would help me know what’s happening here, then I can probably give a more accurate solution.

1 Like

There’s a problem with how the code is structured. When you call the intermission start function and the condition is true, it connects to the remote event and continues onto the intermission end function. All the return statement inside the onserverevent connection (return playerTied) is doing is returning out of the connected function, not returning a value. What I’m trying to say is that .OnServerEvent doesn’t yield the function.

Unless I’m not understanding what you’re trying to do. Could you clarify?

1 Like

Let me try explaining that again.

onIntermissionStart(player) runs and connects a function to tiebreaker.OnServerEvent

then, endIntermission(player) runs. The tietable variable is still empty, as tiebreaker.OnServerEvent HASNT TRIGGERED YET!!!

after that, tiebreaker.OnServerEvent gets triggered and sets the tietable variable

What you can do to fix this:

set tieBreaker.OnServerEvent:Connect outside the function, and inside the onIntermissionStart function, wait until the variable has been updated.

1 Like

IIRC roblox automatically removes tables and stuff to prevent exploiting in roblox, also remotes have limit, make sure your table isn’t too heavy

1 Like

I’m not seeing any “passing” of tables in your code. All I’m seeing is endIntermission being called immediately after onIntermissionStart. Your playerTied table will not be populated with values in this scenario as no time has been allotted for it this to happen. Your onIntermissionStart function is not a yielding function

1 Like

Roblox doesn’t do anything to the tables you pass over the network other than encode them to JSON. You may be referring to the loss of some keys in the table, which is a product of JSON being unable to support mixed tables. Your code shouldn’t be using mixed tables anyway

2 Likes

thanks for correcting me, i mistaked them with metatables

1 Like

Question. When you say the tiebreaker event has not triggered yet, could you explain why? I tried to structure my code such that once I call onIntermissionStart(), the tiebreaker will try to call the client for the choose.targetTile.Name information. And then, we can run the endIntermission, and so on.

Also, when you say set tieBreaker.OnServerEvent:Connect outside the function and inside the onintermissionstart, what does this mean? If I set that outside the function onIntermissionStart(player), then I can’t set it inside the onIntermissionStart(player) again.

Again, I really appreciate the help

1 Like

Basically, the output is that there is an empty table under the endIntermission(player). From enpisie’s suggestion, it seems that the problem is that the tieBreaker:FireServer(choose.targetTile.Name) in the chooseTransport.OnClientEvent:Connect(function() under the client is not triggering until later which is why there is an empty table since nothing is being sent. But, I do not quite understand why.

Thanks. My table is quite light, since it is to keep track of the players who are currently in a tie (they have picked the same tile to stand on during the round).

Sure, I can clarify. This is what I am trying to do. We call the onIntermissionStart() function first. What I want the function to do is handle the event of player ties. In this case, my game is tile based, and the players choose a tile, of course, if 2 players (or more) choose the same tile, then we need to handle the edge case first.

What the function then does is it asks the client every player’s targetTile. This is the tile they have chosen. We update the tietable with the player and thier tile. If 2 or more players have the same tile, then we insert resolve it using the duplicatefinder.

The reason why I want to pass information to the endintermission is because I want tied players to not have the same movement as other players in the game who are not tied. I have tried many different ways of working around this issue, but they have all sadly failed.

Edit: I have tried putting task.wait(1) before calling the endIntermission(player) function, but it seems the table is still empty for some reason.

1 Like

The problem is that onIntermissionStart is not a yielding function. Yes, it fires to the client, but the server will not await a response from that client. It will simply move on. There is a great deal of work that the server will do beyond that point of invoking onIntermissionStart in the noticeable milliseconds associated with the client’s ping. An intermission should last some time. A simple task.wait(INTERMISSION_SECONDS) between the two function calls should suffice

Because .OnServerEvent is an event, it doesn’t yield (aka pause execution).

Additionally, because you’re connecting it every time the intermission starts, after a couple of rounds, it’ll be triggering multiple times. Events don’t disconnect themselves. There are two ways to solve this: by either moving it outside the function, or by disconnecting it after the round ends. I would choose the former, as disconnecting it every time is nothing but extra work.

I suggest you make this a while loop so it waits until every player has submitted.
Below is some example code: (also removed unnecessary indendation)

tieBreaker.OnServerEvent:Connect(function(player, targetTile)
	if not isIntermissionActive then return end

	connectionCount += 1  -- Increment the count of players submitting tiles

	-- Add player and their target tile to tietable
	tietable[player.Name] = tietable[player.Name] or {}
	table.insert(tietable[player.Name], tostring(targetTile))
end)

local function onIntermissionStart(player)
	if isIntermissionActive then return end
	isIntermissionActive = true -- Set the flag to true
	
	-- Only run the logic once all players have submitted
	repeat wait() until connectionCount == #newmodule.storePlayers

	print("All players have submitted their tiles!")
	print(tietable)

	-- Check for duplicates in the tietable
	local duplicates = duplicatefinder.find(tietable)

	if not next(duplicates) then print("no duplicates found") return end		
	
	duplicatefinder.resolve(duplicates)
	print("these are the duplicates", duplicates)
	
	for tile, players in duplicates do
		for _, playerName in players do
			playerTied[playerName] = true
			print(`Player {playerName} tied on tile {tile}`)
		end
	end
	print("Updated playerTied table:", playerTied)
end

Thanks for the suggestion. I have tried it out, but it yielded no fruit. I think I do have a workaround, which I will post soon

Thank you for the suggestion. I have tried putting tiebreaker outside of the function, but my script is not working properly. I appreciate the time you put into this code. I will post my solution which is not really a solution as much as it is a workaround.

Hey everyone. Thank you for the helpful suggestions. I really appreciate it. Here is my workaround solution for the code to work.

The changes I made was I realized that the tieBreaker event relies on the chooseTransport:FireAllClients(player) to fire first before tieBreaker can work. Thus, I placed the function

local function endIntermission(player)

	tileDeleteEvent:FireAllClients(player)
	chooseTransport:FireAllClients(player)
	
end

before the
onIntermissionStart(player) function, since the onIntermissionStart(player) function houses the tieBreaker event. The next change that I had to make was since the tieBreaker event was only for the tied player’s movements, I needed to account for the non-tied players movement properly. Originally, I tried to pass the playerTied table to one of the functions to handle this, but I could not. Thus, I decided instead to create another remoteEvent called testing which fires for players who are not tied to handle their movement.

The code is not so clean right now, but here is the working solution:

Server Code:

local replicatedStorage = game:GetService("ReplicatedStorage")
local newmodule = require(replicatedStorage.Module_Scripts.NewModule)
local starterPlayer = game:GetService("StarterPlayer")

local timerchange = require(replicatedStorage.Module_Scripts.TimerChange)
local intermission = require(replicatedStorage.Module_Scripts.Intermission)
local decalchange = require(replicatedStorage.Module_Scripts.DecalChange)
local duplicatefinder = require(replicatedStorage.Module_Scripts.DuplicateFinder)

local playEvent = replicatedStorage:WaitForChild("PlayEvent")
local tileChangeEvent = replicatedStorage:WaitForChild("tileChangeEvent")
local tileDeleteEvent = replicatedStorage:WaitForChild("tileDeleteEvent")
local players = game:GetService("Players")
local endTimer = replicatedStorage:WaitForChild("EndTimer")
local tieBreaker = replicatedStorage:WaitForChild("TieBreaker")
local chooseTransport = replicatedStorage.RemoteEvents:WaitForChild("chooseTransport")
local tileChangeEvent = replicatedStorage:WaitForChild("tileChangeEvent")
local testing = replicatedStorage.RemoteEvents:WaitForChild("testing")

local isIntermission = false
local playersReady = 0
local isIntermissionActive = false -- Flag to prevent multiple connections

local connectionCount = 0 -- Persist across calls
local tietable = {}
local playerTied = {} -- Track whether each player is tied


local function startIntermission(player)

	tileDeleteEvent:FireAllClients(player)
	chooseTransport:FireAllClients(player)
	
end

local function onIntermissionStart(player)
	if not isIntermissionActive then
		isIntermissionActive = true -- Prevent multiple connections
		
		tieBreaker.OnServerEvent:Connect(function(player, targetTile)
			connectionCount += 1
		
			-- Add player and their target tile to tietable
			tietable[player.Name] = tietable[player.Name] or {}
			table.insert(tietable[player.Name], tostring(targetTile))

			-- When all players have submitted their tiles
			if connectionCount == #newmodule.storePlayers then
				print("All players have submitted their tiles!")
				print(tietable)

				-- Check for duplicates in tietable
				local duplicates = duplicatefinder.find(tietable)

				if next(duplicates) ~= nil then
					duplicatefinder.resolve(duplicates)
					print("Duplicates found:")
					print(duplicates)

					for tile, players in pairs(duplicates) do -- maybe create another fireclient 
						for _, playerName in ipairs(players) do
							playerTied[playerName] = true
							print("Player tied: " .. playerName .. " on tile " .. tile)
						end
						print(playerTied)
						
					end
					print("Updated playerTied table:", playerTied)	
				else 
					print("no duplicates found")
				end
				if not playerTied[player.Name] then
					testing:FireAllClients(player)
				end
			end
		end)
	end
end

local function resetIntermissionState()
	connectionCount = 0
	tietable = {}
	playerTied = {} -- Reset tied status for all players
end

local function roundFunction(player)
	decalchange.initialdecal()
	decalchange.createnew()
	newmodule.TransportonReady(player)
	intermission.start()

	while #newmodule.storePlayers > 0 do
		if not isIntermission then
			isIntermission = true 
			
			tileChangeEvent:FireAllClients(player)
			timerchange.timechange()

			-- Trigger intermission
			intermission.go()
			intermission.countround()
			
			startIntermission(player)
			onIntermissionStart(player) -- Call to handle event connection
			resetIntermissionState()

			decalchange.createnew()
			decalchange.changeit()

			task.wait(3)
			isIntermission = false
		end
	end
end

local function onPlayerReady(player) -- Connect to round function when enough players are ready
	playersReady += 1
	table.insert(newmodule.storePlayers, player)

	if playersReady > 1 then
		roundFunction(player)
	end
end

local function onPlayerDied(player)
	print(player.Name .. " has died.")
	local playerIndex = table.find(newmodule.storePlayers, player)
	if playerIndex then
		table.remove(newmodule.storePlayers, playerIndex)
		print("Removed player " .. player.Name .. " from storePlayers.")
	end
	endTimer:FireClient(player)
end

-- Connect the playEvent to the function
playEvent.OnServerEvent:Connect(onPlayerReady)

-- Ensure to connect player death event
players.PlayerAdded:Connect(function(player)
	player.CharacterAdded:Connect(function(character)
		character:WaitForChild("Humanoid").Died:Connect(function()
			onPlayerDied(player)
		end)
	end)
end)

Client Code:

local replicatedStorage = game:GetService("ReplicatedStorage")
local tileChangeEvent = replicatedStorage:WaitForChild("tileChangeEvent")
local tileDeleteEvent = replicatedStorage:WaitForChild("tileDeleteEvent")
local chooseTransport = replicatedStorage.RemoteEvents:WaitForChild("chooseTransport")
local players = game:GetService("Players")
local RunService = game:GetService("RunService")
local CollectionService = game:GetService("CollectionService")
local tieBreaker = replicatedStorage:WaitForChild("TieBreaker")
local getTeleport = replicatedStorage.RemoteEvents:WaitForChild("getTeleport")
local testing = replicatedStorage.RemoteEvents:WaitForChild("testing")
	
tileChangeEvent.OnClientEvent:Connect(function()
	
	local player = players.LocalPlayer
	if not player then return end -- Ensure we have the local player

	local character = player.Character or player.CharacterAdded:Wait()

	local tilechange = require(character.TileChange)
	local choose = require(character.Choose)
	
	choose.clickit()

	choose.checkhighlight()

	tilechange.cast()
	
	local connection = CollectionService:GetInstanceAddedSignal("Highlight"):Connect(choose.onInstanceAdded)

end)


tileDeleteEvent.OnClientEvent:Connect(function()
	
	local player = players.LocalPlayer
	local character = player.Character or player.CharacterAdded:Wait()
	
	local choose = require(character.Choose)
	local tilechange = require(character.TileChange)
	
	tilechange.clearit()
	
end)

testing.OnClientEvent:Connect(function(player)
	local player = players.LocalPlayer
	local character = player.Character or player.CharacterAdded:Wait()

	local choose = require(character.Choose)
	print("Attempting to transport player") 

	choose.transportplayer() -- Transport logic on the client side
	choose.jumpToTile()
	
end)

chooseTransport.OnClientEvent:Connect(function(player)
	print("connection received")

	local player = players.LocalPlayer
	local character = player.Character or player.CharacterAdded:Wait()

	local choose = require(character.Choose)
	print("Attempting to transport player") 

	choose.transportplayer() -- Transport logic on the client side
	--choose.jumpToTile()

	-- Debugging `choose.targetTile`
	if choose.targetTile then
		print("Target tile object:", choose.targetTile) 
		print("Target tile name:", choose.targetTile.Name)
		tieBreaker:FireServer(choose.targetTile.Name) 
	else
		print("choose.targetTile is nil!") 
		warn("No target tile selected!") 
	end
end)


getTeleport.OnClientInvoke = function(player) 
	print("the client has been invoked")
	local player = game.Players.LocalPlayer
	local character = player.Character or player.CharacterAdded:Wait()

	local choose = require(character.Choose)

	-- Ensure that choose.targetPosition is correctly set before invoking
	local position = choose.transportplayer(player)

	-- Debugging print to ensure you are getting a valid position
	print("Returning position to server:", position)

	return position
end

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