Optimization for spawn system

So I am currently working on a spawn system for my building game that I am working on, this code does work, however it is kinda slow, I’m wondering if there’s a way it could be faster?

It can be faster if I remove the wait in the IsValidWeld, however then it would just freeze the client for a bit, and I’m afraid that would just disconnect them.

game.ReplicatedStorage.Remotes.Spawn.OnServerInvoke = function(Player)
	local success, result = pcall(function()
		local Plot = GetPlayerPlot(Player)

		if not Plot then
			return false
		end
		
		local BuildName = Player.Name .. tostring(Player.UserId) .. "_Build"
		local PlotName = tostring(Player.UserId) .. "_Plot"
		
		if not game.Workspace:FindFirstChild(BuildName) then
			if not game.ServerStorage:FindFirstChild(PlotName) then
				local Folder = Instance.new("Folder", game.ServerStorage)
				Folder.Name = PlotName
			end
			
			local Build = Instance.new("Model", game.Workspace)
			Build.Name = BuildName

			for _,v in ipairs(Plot:GetChildren()) do
				if v:IsA("Model") then
					v.Parent = game.ServerStorage[PlotName]
					
					local CopyModel = v:Clone()
					CopyModel.Parent = Build
				end
			end
			
			local function IsWeldValid(P0, P1)
				wait()
				
				for _,v:WeldConstraint in ipairs(Build:GetChildren()) do
					if v:IsA("WeldConstraint") then
						if (v.Part0 == P0 and v.Part1 == P1) or (v.Part0 == P1 and v.Part1 == P0) then
							return false
						end
					end
				end
				
				return true
			end
			
			for _,v:Model in ipairs(Build:GetChildren()) do
				if v:IsA("Model") then
					local CF, Size = v:GetBoundingBox()
					local Params = OverlapParams.new()
					Params.FilterDescendantsInstances = {v}
					Params.FilterType = Enum.RaycastFilterType.Exclude
					
					local PartsInBlock = workspace:GetPartBoundsInBox(CF, Size - Vector3.new(1, 1, 1), Params)
					
					for _,j in ipairs(PartsInBlock) do
						if j:IsDescendantOf(Build) then
							local MainModel = j:IsA("Model") and j or j:FindFirstAncestorOfClass("Model")
							
							if not IsWeldValid(v.Main, MainModel.Main) then
								local Weld = Instance.new("WeldConstraint")
								Weld.Parent = Build
								Weld.Part0 = v.Main
								Weld.Part1 = MainModel.Main
							end
						end
					end
					
					for _,j:Model in ipairs(Build:GetChildren()) do
						if j:IsA("Model") then
							if j ~= v then
								for _,k:Attachment in ipairs(j.PrimaryPart:GetChildren()) do
									if k:IsA("Attachment") and k.Name == "ConnectingPoint" then
										for _,l:Attachment in ipairs(v.PrimaryPart:GetChildren()) do
											if l:IsA("Attachment") and l.Name == "ConnectingPoint" then
												if l.WorldCFrame.Position == k.WorldCFrame.Position then
													if IsWeldValid(v.Main, j.Main) then
														local Weld = Instance.new("WeldConstraint")
														Weld.Parent = Build
														Weld.Part0 = v.Main
														Weld.Part1 = j.Main
													end
												end
											end
										end
									end
								end
							end
						end
					end
				end
			end

			for _,v in ipairs(Build:GetDescendants()) do
				if v:IsA("BasePart") or v:IsA("UnionOperation") then
					if v.Name == "PlacementBlock" then
						v:Destroy()
					else
						v.Anchored = false
					end
				end
			end
			
			return true
		else
			return false
		end
	end)
	
	if not success then
		print(result)
		
		return false
	else
		return result
	end
end```
2 Likes

image

This makes me want to cry :sob:

Please separate your code into multiple functions, and use and instead of spamming if.

Also, instead of keeping executed code inside the if-statement, just turn that if-statement into a negation, and if that negation is true, use continue to not run the next bit of code.

1 Like

My bad about the end hell thing, I completely forgot continue existed. I cleaned some of it up, however I’m not entirely sure what you mean about separating it into multiple functions. Here’s the new code fo it.

game.ReplicatedStorage.Remotes.Spawn.OnServerInvoke = function(Player)
	local success, result = pcall(function()
		local Plot = GetPlayerPlot(Player)
		
		local BuildName = Player.Name .. tostring(Player.UserId) .. "_Build"
		local PlotName = tostring(Player.UserId) .. "_Plot"
		
		if not Plot or game.Workspace:FindFirstChild(BuildName) then return false end
		
		if not game.ServerStorage:FindFirstChild(PlotName) then
			local Folder = Instance.new("Folder", game.ServerStorage)
			Folder.Name = PlotName
		end

		local Build = Instance.new("Model", game.Workspace)
		Build.Name = BuildName

		for _,v in ipairs(Plot:GetChildren()) do
			if not v:IsA("Model") then continue end

			v.Parent = game.ServerStorage[PlotName]

			local CopyModel = v:Clone()
			CopyModel.Parent = Build
		end

		local function IsWeldValid(P0, P1)
			for _,v:WeldConstraint in ipairs(Build:GetChildren()) do
				if not v:IsA("WeldConstraint") then continue end

				if (v.Part0 == P0 and v.Part1 == P1) or (v.Part0 == P1 and v.Part1 == P0) then
					return false
				end
			end

			return true
		end

		for _,v:Model in ipairs(Build:GetChildren()) do
			if not v:IsA("Model") then continue end

			local CF, Size = v:GetBoundingBox()
			local Params = OverlapParams.new()
			Params.FilterDescendantsInstances = {v}
			Params.FilterType = Enum.RaycastFilterType.Exclude

			local PartsInBlock = workspace:GetPartBoundsInBox(CF, Size - Vector3.new(1, 1, 1), Params)

			for _,j in ipairs(PartsInBlock) do
				if not j:IsDescendantOf(Build) then continue end

				local MainModel = j:IsA("Model") and j or j:FindFirstAncestorOfClass("Model")

				if not IsWeldValid(v.Main, MainModel.Main) then continue end
				
				local Weld = Instance.new("WeldConstraint")
				Weld.Parent = Build
				Weld.Part0 = v.Main
				Weld.Part1 = MainModel.Main
				
				wait()
			end

			for _,j:Model in ipairs(Build:GetChildren()) do
				if not j:IsA("Model") or j == v then continue end

				for _,k:Attachment in ipairs(j.PrimaryPart:GetChildren()) do
					if not k:IsA("Attachment") or k.Name ~= "ConnectingPoint" then continue end

					for _,l:Attachment in ipairs(v.PrimaryPart:GetChildren()) do
						if (not l:IsA("Attachment") or l.Name ~= "ConnectingPoint") or (l.WorldCFrame.Position ~= k.WorldCFrame.Position) then continue end

						if not IsWeldValid(v.Main, j.Main) then continue end
						
						local Weld = Instance.new("WeldConstraint")
						Weld.Parent = Build
						Weld.Part0 = v.Main
						Weld.Part1 = j.Main
						
						wait()
					end
				end
			end
		end

		for _,v in ipairs(Build:GetDescendants()) do
			if not v:IsA("BasePart") and not v:IsA("UnionOperation") then continue end

			if v.Name == "PlacementBlock" then
				v:Destroy()
			else
				v.Anchored = false
			end
		end

		return true
	end)
	
	if not success then
		print(result)
		
		return false
	else
		return result
	end
end
1 Like

One quick change that should help a bit is parenting objects after you’re done mutating them versus before.

local Weld = Instance.new("WeldConstraint")
Weld.Parent = Build
Weld.Part0 = v.Main
Weld.Part1 = MainModel.Main

is better written as:

local Weld = Instance.new("WeldConstraint")
Weld.Part0 = v.Main
Weld.Part1 = MainModel.Main
Weld.Parent = Build

Out of curiosity, why do you have a wait() in your code, that will certainly slow things down.

for _,j:Model in ipairs(Build:GetChildren()) do
	if not j:IsA("Model") or j == v then continue end

	for _,k:Attachment in ipairs(j.PrimaryPart:GetChildren()) do
		if not k:IsA("Attachment") or k.Name ~= "ConnectingPoint" then continue end

		for _,l:Attachment in ipairs(v.PrimaryPart:GetChildren()) do
			...
			
			wait()
		end
	end
end

Not sure what that’s achieving. Are you getting an error for the script running too long so you added a wait?

for _,v in ipairs(Plot:GetChildren()) do
	if not v:IsA("Model") then continue end

	v.Parent = game.ServerStorage[PlotName]

	local CopyModel = v:Clone()
	CopyModel.Parent = Build
end

Why not just copy the Plot instead of copying every child independently here? I see you’re not copying models so I’m sort of curious what you’re copying into Build here… if your script is slow then you must be copying quite a lot, in which case it may actually be faster to copy the whole Plot and delete the models after cloning.

Aside from that, I’m not too sure what’s slow here, unless your Build is just an absolutely massive model.

I probably should’ve been more clear here, to answer your questions:

Without the wait() would be faster, however depending on how large their build is, it will freeze the client, and if frozen for too long, would probably disconnect them.

That is a great idea, while that part is not really the part that is doing the freezing, or at least most of it, that would be a better solution for copying the Plot, also if you look closely at the script, I am only copying models, it’s negated so if it’s not a model, it will continue the loop and ignore the rest of the code. Thanks to @iGottic for suggesting that.

Anyways referring to the part that is mainly doing the freezing is the weld for loop itself. Sorry if I’m still not very clear.