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?
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.
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?
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
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
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.
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.
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
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