Features not working with 3+ players

I’m making an obby game that includes wall jumps like in FE2, launch pads, etc. Also I have in the lobby a training room whose door tweens opened nicely. I also have it so the lobby changes to winter theme automatically during the winter season.

The wall jumps, launch pads, and training room door work perfectly fine if I’m playing alone. But, when I tested with 3 other people none of them worked. I tested again with just me and my alt and all the features worked perfectly fine.
The lobby also immediately changed to winter theme when I was playing alone or with an alt, but with 3+ people it took once maybe 10 seconds to change, then once it took approximately a minute.

Only possible problem I can think of is that I am utilizing coroutines with an infinite loop in it (which does slow all scripts down), but I’m not sure of an alternative to coroutines…

task.spawn(function()

I tried task.spawn, but it just isn’t my solution.
My problem is I need something that doesn’t yield my script, and at the same time I need to be able to cancel the function at my leisure.

You can cancel using task.cancel.

We also need some code to see.

Here’s the part where coroutines are being used:
(coroutines aren’t being created here, just being run and/or stopped)

-- existingeffects is a list of dictionaries; the d.Function is obviously the function I'm trying to execute.
while wait() do 
	for a,b in pairs(folder:GetChildren()) do 
		for i,v in pairs(b:GetChildren()) do
			if (v:IsA("BasePart") or v:IsA("UnionOperation") or v:IsA("MeshPart")) and v:FindFirstChildOfClass("AlignPosition") then
				if (v:FindFirstChildOfClass("AlignPosition").Attachment0.WorldPosition - v:FindFirstChildOfClass("AlignPosition").Attachment1.WorldPosition).Magnitude > 50 then
					v.Position = v:FindFirstChildOfClass("AlignPosition").Attachment1.WorldPosition
				end
			end
		end
	end
	for c,d in pairs(existingEffects) do
		if d.Player and game.Players:FindFirstChild(d.Player.Name) then
			playerIsAtSpawn = checkIfPlayerIsInBounds(d.Player, spawnArea)
			playerIsInVIPLounge = checkIfPlayerIsInBounds(d.Player, workspace.LobbyStuff.VIPArea)

			if playerIsAtSpawn == true and d.Running == true and d.RC == true then 
				forceStopEffect(d,c)

				if d.Player == game.Players.LocalPlayer and game.Players.LocalPlayer:FindFirstChild("PlayerGui") then 
					loadUIStatement("Your effect has been disabled while you are at spawn.")
				end

				continue
			elseif playerIsInVIPLounge == true  and d.Running == true and d.RC == true then
				forceStopEffect(d,c)

				if d.Player == game.Players.LocalPlayer and game.Players.LocalPlayer:FindFirstChild("PlayerGui") then 
					loadUIStatement("Your effect has been disabled while you are in the VIP lounge.")
				end

				continue
			elseif userSettings:GetAttribute("HideEffects") == 3 and table.find(_G.Contestants, d.Player) and workspace.CurrentMap:FindFirstChildOfClass("Folder") then
				forceStopEffect(d,c)
				continue
			end

			if d.Running == true and d.RC ~= true then
				if playerIsAtSpawn == false and playerIsInVIPLounge == false then
					if effectsAreToggled == true then
						coroutine.resume(d.Function)
						d.RC = true
					end
				end
			elseif d.Running == false and d.RC == true then
				coroutine.close(d.Function);
				d.RC = false

				table.remove(existingEffects, c)
			end
		else
			if d.Running == true and d.RC == true then
				coroutine.close(d.Function)
			end
			table.remove(existingEffects, c)
		end
	end

end

Hopefully you can see now why task.spawn isn’t working out for me. I did use task.cancel when I tried out task.spawn, but because of task.spawn yielding, task.cancel serves useless.

The whole point of task.spawn is that it runs in a new thread, therefore not yielding. I’m assuming you were using it incorrectly.

I fixed a couple of things with your script:

  1. You should be using task.wait instead of wait
  2. pairs/ipairs should be removed and replaced with generalized iteration as it runs faster
  3. You had checks for v:IsA("MeshPart") and v:IsA("UnionOperation") but then had v:IsA("BasePart")
    The first two checks are useless, because MeshParts and UnionOperations are BaseParts
  4. You should save the results of :FindFirstChild so you don’t waste performance

Code:

-- existingeffects is a list of dictionaries; the d.Function is obviously the function I'm trying to execute.
while task.wait() do 
	for a, b in folder:GetChildren() do 
		for i, v in b:GetChildren() do
			if v:IsA("MeshPart") then
				local AlignPosition = v:FindFirstChildOfClass("AlignPosition")
				
				if AlignPosition and (AlignPosition.Attachment0.WorldPosition - AlignPosition.Attachment1.WorldPosition).Magnitude > 50 then
					v.Position = AlignPosition.Attachment1.WorldPosition
				end
			end
		end
	end
	for c,d in existingEffects do
		if d.Player and game.Players:FindFirstChild(d.Player.Name) then
			playerIsAtSpawn = checkIfPlayerIsInBounds(d.Player, spawnArea)
			playerIsInVIPLounge = checkIfPlayerIsInBounds(d.Player, workspace.LobbyStuff.VIPArea)

			if playerIsAtSpawn == true and d.Running == true and d.RC == true then 
				forceStopEffect(d,c)

				if d.Player == game.Players.LocalPlayer and game.Players.LocalPlayer:FindFirstChild("PlayerGui") then 
					loadUIStatement("Your effect has been disabled while you are at spawn.")
				end

				continue
			elseif playerIsInVIPLounge == true  and d.Running == true and d.RC == true then
				forceStopEffect(d,c)

				if d.Player == game.Players.LocalPlayer and game.Players.LocalPlayer:FindFirstChild("PlayerGui") then 
					loadUIStatement("Your effect has been disabled while you are in the VIP lounge.")
				end

				continue
			elseif userSettings:GetAttribute("HideEffects") == 3 and table.find(_G.Contestants, d.Player) and workspace.CurrentMap:FindFirstChildOfClass("Folder") then
				forceStopEffect(d,c)
				continue
			end

			if d.Running == true and d.RC ~= true then
				if playerIsAtSpawn == false and playerIsInVIPLounge == false then
					if effectsAreToggled == true then
						coroutine.resume(d.Function)
						d.RC = true
					end
				end
			elseif d.Running == false and d.RC == true then
				coroutine.close(d.Function);
				d.RC = false

				table.remove(existingEffects, c)
			end
		else
			if d.Running == true and d.RC == true then
				coroutine.close(d.Function)
			end
			table.remove(existingEffects, c)
		end
	end

end

Thanks for pointing those errors out. I did find myself doing something wrong with the task.spawn: corrected my issue and it is no longer yielding. Won’t be able to mark this as a solution until I can test this out with other people… :sweat_smile:

Least obvious AI generated reply

1 Like

Thanks for that list. Regarding the stuff like wall jumps and launch pads, I am only using one loop which simply checks if the player hits the wall (since touched events are not ideal), but that shouldn’t exactly be a big problem, should it?

Okay I finally got around to testing this out with 3+ people. Switching to using task instead of coroutines appears to have solved one issue, but it has yet to solve the others.
The wall jumps and launchpads I’ve discovered will work randomly, but the chances are rather low (when there are 3+ people, works fine if there are two or lower); however, if I reset my character they work fine (the scripts are located under StarterCharacterScripts). Since the script just uses a for loop and :GetDescendants() to find wall jumps and launch pads in the map, I think it is safe to assume this is a loading issue? However, I did put a wait(3) once a map is added into the workspace and that really didn’t fix the issue.

The lobby also continues to take up to seconds to switch when there are multiple people.

Could you show me the code for the wall jumps and launchpads?

Here’s how the script detects wall jumps and launch pads; the actual code for the wall jumps and launch pads works fine, it’s just that it won’t detect anything when the map is first added. I have to reset my character to get it to work (the script is a local script located under StarterCharacterScripts).

I had put a wait(3) in front in case maybe the map is slow to load in, but it didn’t seem to work at all. The same issue occurs with kill bricks that I have in my game too.

local map = workspace.CurrentMap:FindFirstChildOfClass("Folder")
repeat wait() map = workspace.CurrentMap:FindFirstChildOfClass("Folder") until map

local activeWallJumps = {}

wait(3)
for _,model in pairs(map:GetDescendants()) do
	if model:IsA("Model") then
		if string.lower(model.Name) == "launchpad" and model:FindFirstChild("Destination") and model:FindFirstChild("Pad") then
			-- Launchpads
			local pad, destination = model:WaitForChild("Pad"), model:WaitForChild("Destination")
			local debounce
			
			
			pad.Touched:connect(function(hit)
				-- And here goes a bunch of code for the launchpads.
			end)
		end
	elseif model:IsA("BasePart") then
		if string.lower(model.Name) == "walljump" then
			-- WallJumps
			table.insert(activeWallJumps, model)  -- A while loop handles whether a player is on a walljump or not using this activeWallJumps table.
		end
	end
end

Uhhhhhh, you probably shouldn’t be making features related to obstacles on the client. This changes pretty much how I saw your code.

You should move these features to the server, and it should start working better because it doesn’t depend on the client.

The features actually do rely on the client; I’ve hid the code though. Would it be just as feasible to put the launchpads and wall jumps in a table in the server and send the table over to the client?

Yeah, which is why I said you probably shouldn’t be making features related to the obstacles on the client.

Why can’t you just get all the launchpads on the server without the dependency of the client?

The launch pads use LinearVelocity to launch players from a launch pad to a specific destination in a semi-elliptical pattern, which needs to be accurate for an obstacle course game. I’ve already tried scripting it on the server prior to this post and found it didn’t work because the character’s position on the server and on the client can be different, causing the path to be inaccurate…

As for the wall jumps, they don’t rely on touched events since touched events don’t always fire so it relies on a while loop instead (which, for the same reason above, scripting it on the server wouldn’t be very ideal). And I’m also using UserInputService to detect when a player wants to jump off the wall.