Tips on optimizing welding module

local function IsDescendantOfCharacter(part)
	local ancestor = part
	while ancestor and ancestor.Parent do
		if ancestor:IsA("Model") and Players:GetPlayerFromCharacter(ancestor) then
			return true
		end
		ancestor = ancestor.Parent
	end
	return false
end

local function InSameModel(partA, partB)
	local rootA = partA:FindFirstAncestorOfClass("Model")
	local rootB = partB:FindFirstAncestorOfClass("Model")
	return rootA and rootA == rootB
end

local function CacheKeyExists(partA, partB)
	local cacheA = WeldCache[partA]
	local cacheB = WeldCache[partB]
	return (cacheA and cacheA[partB]) or (cacheB and cacheB[partA])
end

local function CacheKeyAdd(partA, partB)
	WeldCache[partA] = WeldCache[partA] or setmetatable({}, { __mode = "k" })
	WeldCache[partA][partB] = true
	WeldCache[partB] = WeldCache[partB] or setmetatable({}, { __mode = "k" })
	WeldCache[partB][partA] = true
end

local function CheckIfCanWeld(partA, partB)
	if partA == partB then return false end
	if CacheKeyExists(partA, partB) then return false end
	if InSameModel(partA, partB) then return false end

	for _, weld in ipairs(partB:GetChildren()) do
		if weld:IsA("WeldConstraint") then
			if (weld.Part0 == partA and weld.Part1 == partB) or (weld.Part0 == partB and weld.Part1 == partA) then
				CacheKeyAdd(partA, partB)
				return false
			end
		end
	end

	return true
end

local function CreateWeld(partA, partB)
	if IsDescendantOfCharacter(partA) or IsDescendantOfCharacter(partB) then
		DebugPrint("Skipped welding character part:", partA.Name, partB.Name)
		return
	end

	if CheckIfCanWeld(partA, partB) then
		local weld = Instance.new("WeldConstraint")
		weld.Part0 = partA
		weld.Part1 = partB
		weld.Parent = partA
		CacheKeyAdd(partA, partB)
		DebugPrint("Welded:", partA.Name, "<->", partB.Name)
	end
end

local function SurfaceWeldPart(part)
	if not part:IsA("BasePart") then return end

	Params.FilterDescendantsInstances = { part }

	local partsNearby = workspace:GetPartBoundsInBox(part.CFrame, part.Size + OffsetVector, Params)
	for _, nearbyPart in ipairs(partsNearby) do
		CreateWeld(part, nearbyPart)
	end
end

function SurfaceWeldModule.WeldPart(part)
	SurfaceWeldPart(part)
end

function SurfaceWeldModule.WeldModel(model)
	for _, part in ipairs(model:GetDescendants()) do
		SurfaceWeldPart(part)
	end
end

function SurfaceWeldModule.WeldParts(partsArray)
	for _, part in ipairs(partsArray) do
		SurfaceWeldPart(part)
	end
end

function SurfaceWeldModule.ClearWelds(target)
	local parts = {}

	if typeof(target) == "Instance" then
		parts = target:GetDescendants()
	elseif typeof(target) == "table" then
		parts = target
	end

	for _, part in ipairs(parts) do
		if part:IsA("BasePart") then
			for _, child in ipairs(part:GetChildren()) do
				if child:IsA("WeldConstraint") then
					child:Destroy()
					DebugPrint("Destroyed weld on:", part.Name)
				end
			end
		end
	end
end

function SurfaceWeldModule.ClearCache()
	table.clear(WeldCache)
	DebugPrint("Cleared WeldCache")
end

function SurfaceWeldModule.SetDebug(state)
	DEBUG = state
end

My welding module welds any parts that are touching each other whenever a block is placed. It has a weld cache system for parts and it uses workspace:GetPartBoundsInBox() to find touching parts.

I tried placing a lot of blocks to see how much it can handle, and it starts causing performance issues at around 300 blocks if they are placed inside each other. If they are placed ontop of each other it doesnt lag as much at around 1,000 blocks but when the player zooms in it causes lag.

I would like suggestions on how I can improve this so it causes minimal lag even if a lot of blocks are welded.

1 Like