So I’ve made this script that gives all the players their necessary things and basically makes the whole game function and makes sure that the teachers and students have their necessary settings adjusted (Guis, seats, etc). However, there are multiple bugs.
For one, the while true do statement will randomly pause, as seen by the timer section that will stop counting after a random time.
Also, I need to account for players that join after the main loop that calls establishSettings() for each player.
I also need help with making sure that multiple players aren’t placed into the same seat. I have tried to fix this by removing a player’s seat from the available seats when it is assigned, but it still persists.
And I’ve received reports that the the TimerLabel’s text will show the message that displays when there are less than 2 players, even when the server has 2 or more.
Here is the script:
while true do
local allPlayers = Players:GetPlayers()
if #allPlayers < 2 then
for _, player:Player in allPlayers do
local playerGui = player:WaitForChild("PlayerGui")
local timer = playerGui:WaitForChild("GlobalGui").TimerLabel
timer.Text = "2 players required to start game..."
end
task.wait(0.01)
continue
end
local teacherIndex = math.random(1, #allPlayers)
local teacher = allPlayers[teacherIndex]
-- Assign teacher
teacher.Team = Teams.Teachers
local teacherCharacter:Model = teacher.Character or teacher.CharacterAdded:Wait() --wait for teacher character
local teacherHumanoid:Humanoid = teacherCharacter.Humanoid
teacherHumanoid.JumpPower = 50 --allow the teacher to jump
teacherHumanoid:ChangeState(Enum.HumanoidStateType.Jumping)
teacherCharacter:MoveTo(teacherSpawnpoint.Position) --move teacher character to teacherspawnpoint
local ruler:Tool = teacher.Backpack:FindFirstChild("Ruler",false)
--check if user has ruler tool
if not ruler then --if not, then give them the tool
local newTool = game.ReplicatedStorage.Ruler:Clone()
newTool.Parent = teacher.Backpack
end
local function establishSettings(player:Player)
if player ~= teacher then --if player is not the teacher (means they are a student)
player.Team = Teams.Students --make player a student
print("Function called for:" .. player.Name)
local character = player.Character or player.CharacterAdded:Wait()
local studentHumanoid:Humanoid = character:WaitForChild("Humanoid")
--get student's humanoid
local StudentSeatValue:ObjectValue = player.StudentSeatValue--get student seat value
local seat = studentSeats[#allPlayers]
print(seat)
StudentSeatValue.Value = seat
print(StudentSeatValue.Value)
studentHumanoid.JumpPower = 0
seat:Sit(studentHumanoid) --lock player into seat
for i, item in studentSeats do --REMOVE SEAT FROM STUDENTSEATS
if item == seat then --SO TWO PLAYERS DONT GET ASSIGNED]
table.remove(studentSeats,i) --THE SAME SEAT
end
end
local ruler = player.Backpack:FindFirstChild("Ruler") --check if user has ruler
if ruler then
ruler:Destroy() --destroy if user has ruler
end
else
local teacherSeat = player:FindFirstChild("StudentSeatValue")
addToTable(studentSeats,teacherSeat) --add teachers studentseatvalue to studentseats table
--so it can be reused
end
end
Players.PlayerRemoving:Connect(function(player)
local seatValue = player:FindFirstChild("StudentSeatValue")
addToTable(studentSeats,seatValue)
end)
for _, player:Player in allPlayers do
establishSettings(player)
end
local minutes = 3
local tenSeconds = 0
local seconds = 0
repeat
for _, player:Player in allPlayers do
local playerGui = player:WaitForChild("PlayerGui")
local timer = playerGui:WaitForChild("GlobalGui").TimerLabel
timer.Text = minutes .. ":" .. tenSeconds .. seconds
end
seconds -= 1
if seconds < 0 then
tenSeconds -= 1
seconds = 9
end
if tenSeconds < 0 then
minutes -= 1
tenSeconds = 5
end
task.wait(1)
until minutes < 0
end
so for your last issue, it looks like if you have less than 2 players, it will make the GUI show up for all players, but there’s never any logic that hides it again if there are 2 or more players.
for your first issue, have you checked the errors? Probably it just can’t find one of the player’s GUI or something, it errors, and it breaks the whole script.
the seat stuff is kinda confusing, but you could prob just do this:
local seat = studentSeats[#allPlayers]
studentSeats[#allPlayers] = nil
but, its somewhat confusing what you are trying to do, since im pretty sure its just going to try and call the same seat over and over again, because #allPlayers number isnt going to change. Maybe this would work better:
local seat = studentSeats[#studentSeats]
studentSeats[#studentSeats] = nil
Id recommend using remote events, or some value everyone can see, really anything to make it so the client handles its own GUI and not the server, but ignoring that, just make it skip over players where their playerGUI isnt found using :FindFirstChild so it doesn’t yield
Your whole code is super messy and subject to heavy peformance issues and memory leak.
The establishSettings function and the PlayerRemoving signal should be outside of the loop to start with. The players timer GUI should be changed in client side. The repeat until loop actually stop the while loop until 3 minutes has elapsed.
Any WaitForChild() call can yield, and so will the teacher.CharacterAdded:Wait().
I’d suggest using FindFirstChild() and and skip the code that involves the child if the child is nil.
Another thing you could do is wrap things like this to create a new thread.
task.spawn(function()
-- code
end)
This will run the inside code on a new thread that gets ran independently, and allows the outside to continue without waiting for the inside code.
Alright, but there are better and more optimized methods that don’t require to put a loop inside another. One single loop should be enough for a round based gameplay.
Let me some minutes to show an exemple using your code. (maybe a little more time, there’s a studio update ^^)
That code looks fine to me and is a clear way to program the gameloop. The counting is oldschool but the inner loop is fine. It’s unrelated to the problem here so we probably shouldn’t focus on it.
The current code is susceptible to memory leaks caused by duplicate functions and connections on each loop restart. Additionally, errors may occur due to the absence of condition checks that verify the existence of certain instances. There could also be significant performance issues resulting from unoptimized iterations.
But that’s okay, I’m not here to criticize, I just want to help… it’s just that it would be easier for me to edits the code and show an exemple instead of writing everything as it would be too long to explain
In short, in my opinion, a round-based code should consist of a single infinite loop that primarily serves to pass time and call functions.
I’d suggest using an “if” statement and ignoring the code that would use the child.
You can think about it more deeply, and if the code is a necessity to run, you might need to do something more complicated. In the case that it’s updating UI to reflect the time, then I’d ignore it.
I pretty much tried to clean up the code as best as I could. Feel free to tell me if it work, edits it as you wish or ask questions.
Server Script
-- Roblox Services --
local PlayerService = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local TeamService = game:GetService("Teams")
-- Primary Instances --
local RulerTool = ReplicatedStorage:WaitForChild("Ruler")
local RoundTimer = Instance.new("IntValue")
local Status = Instance.new("StringValue")
--------------------------------------------------
-- Local Functions --
local function SetupStudents(teacher)
local playerList = PlayerService:GetPlayers()
table.remove(playerList, table.find(playerList, teacher))
for _, player in playerList do
local studentcharacter: Model = player.Character or player.CharacterAdded:Wait(10)
local studentHumanoid: Humanoid = studentcharacter:WaitForChild("Humanoid", 10)
local studentBackpack: Backpack = player and player:FindFirstChildWhichIsA("Backpack")
local studentSeatValue = player and player:FindFirstChild("StudentSeatValue")
local ruler: Tool = studentBackpack and studentBackpack:FindFirstChild("Ruler")
local seat = studentSeats[#PlayerService:GetChildren()]
player.Team = TeamService.Students
if studentcharacter and studentHumanoid and studentBackpack and studentSeatValue then
studentSeatValue.Value = seat
studentHumanoid.JumpPower = 0
seat:Sit(studentHumanoid)
for i, item in studentSeats do
if item == seat then
table.remove(studentSeats, i)
end
end
if ruler then
ruler:Destroy()
end
end
end
end
local function SetupTeacher()
local playerList = PlayerService:GetPlayers()
local teacher = playerList[math.random(1, #playerList)]
local teacherCharacter: Model = teacher.Character or teacher.CharacterAdded:Wait(10)
local teacherHumanoid: Humanoid = teacherCharacter:WaitForChild("Humanoid", 10)
local teacherBackpack: Backpack = teacher and teacher:FindFirstChildWhichIsA("Backpack")
local teacherSeat = teacher and teacher:FindFirstChild("StudentSeatValue")
local ruler: Tool = teacherBackpack and teacherBackpack:FindFirstChild("Ruler") or RulerTool:Clone()
if teacher and teacherCharacter and teacherHumanoid and teacherBackpack and teacherSeat and ruler then
ruler.Parent = teacherBackpack
teacher.Team = TeamService.Teachers
teacherHumanoid.JumpPower = 50
teacherHumanoid:ChangeState(Enum.HumanoidStateType.Jumping)
teacherHumanoid:MoveTo(teacherSpawnpoint.Position)
addToTable(studentSeats, teacherSeat)
return teacher
else
return nil
end
end
--------------------------------------------------
-- Signal Connections --
PlayerService.PlayerRemoving:Connect(function(OldPlayer)
local seatValue = OldPlayer:FindFirstChild("StudentSeatValue")
if seatValue then
addToTable(studentSeats, seatValue)
end
end)
-- Setup Values --
-- Create new values in the workspace as a reference for clients
RoundTimer.Parent = workspace
RoundTimer.Name = "RoundTimer"
RoundTimer.Value = 300
Status.Parent = workspace
Status.Name = "Status"
Status.Value = "WaitForPlayers"
-- Main Loop --
while task.wait(1) do
if Status.Value == "WaitForPlayers" and #PlayerService:GetPlayers() >= 2 then -- Start a new game
local teacher = SetupTeacher()
if teacher then
SetupStudents(teacher)
Status.Value = "IsPlaying"
RoundTimer.Value = 300
end
elseif Status.Value == "IsPlaying" and RoundTimer.Value > 0 then -- It is playing so just change timer
RoundTimer.Value -= 1
elseif Status.Value == "IsPlaying" and RoundTimer.Value <= 0 then -- Stop the game
Status.Value = "WaitForPlayers" --or implement a "intermission"
end
end
local script for text
local RoundTimer = workspace:WaitForChild("RoundTimer")
local Status = workspace:WaitForChild("Status")
local TimerLabel = script.Parent
local function TimerFormat(Seconds: number)
local Minutes = (Seconds - Seconds%60) / 60
Seconds = Seconds - Minutes * 60
return string.format("%02i", Minutes)..":"..string.format("%02i", Seconds)
end
RoundTimer:GetPropertyChangedSignal("Value"):Connect(function()
if Status.Value == "IsPLaying" then
TimerLabel.Text = TimerFormat(RoundTimer.Value)
end
end)
Status:GetPropertyChangedSignal("Value"):Connect(function()
if Status.Value == "IsPLaying" then
TimerLabel.Text = TimerFormat(RoundTimer.Value)
elseif Status.Value == "WaitForPlayers" then
TimerLabel.Text = "2 players required to start game..."
end
end)
Nop, I did not added it because I didn’t knew if there were a lobby or if joining players would dirrectly join the current round. If needed, you can add a new connection for it:
PlayerService.PlayerAdded:Connect(function(NewPlayer)
if Status.Value == "IsPlaying" then
-- Do everything needed to join the game
elseif Status.Value == "WaitForPlayers" then
-- Join lobby?
end
end)
I also suggest to manually add a Configuration into the workspace, in which you can add both the Status and RoundTimer values.
This way, you can replace the top variables to this:
local Configuration = workspace:WaitForChild("Configuration")
local RoundTimer = Configuration:WaitForChild("RoundTimer")
local Status = Configuration:WaitForChild("Status")
and completely remove the following lines, which save up some space and make the bottom script feel more proper and readable.
-- Setup Values --
-- Create new values in the workspace as a reference for clients
RoundTimer.Parent = workspace
RoundTimer.Name = "RoundTimer"
RoundTimer.Value = 300
Status.Parent = workspace
Status.Name = "Status"
Status.Value = "WaitForPlayers"