Big Tycoon lagging due to many parts

Hi I am making this post today seeking for help
Im making a big tycoon with many droppers but its causing lag (high ping and CPU)

Here’s the script, if theres anything wrong please tell me
btw its being fired to all clients

local TweenService = game:GetService("TweenService")
local RunService = game:GetService("RunService")

local Main = require(game.ReplicatedStorage.Framework)

local PlotsFolder = workspace:WaitForChild("Plots")

local PlayerDataFunction : RemoteFunction = Main:GetElement("PlayerDataFunction")

local Refinery = {}

local SellDebounce = false

function comma_value(amount)
	local formatted = amount
	while task.wait() do  
		formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
		if (k==0) then
			break
		end
	end
	return formatted
end

local function InputOre(Player, Ore)
	table.insert(Refinery[Player], Ore)
	Ore:Destroy()
end

local CurrentMoney = 0

local function OutputOre(Player, RefineryOutput : BasePart)
	local OreTable = Refinery[Player]
		
	if #OreTable > 0 then
		local OreToRefine = OreTable[1]
		table.remove(Refinery[Player], 1)
		local RefinedOre = Main:GetElement("Refined" .. OreToRefine.Name):Clone()
		
		if RefinedOre:IsA("BasePart") then
			RefinedOre.CollisionGroup = "Ores"
		else
			for _, Parts in pairs(RefinedOre:GetDescendants()) do
				if Parts:IsA("BasePart") then
					Parts.CollisionGroup = "Ores"
				end
			end
		end
		
		RefinedOre:SetAttribute("IsRefined", true)
		RefinedOre:SetAttribute("Multiplier", OreToRefine:GetAttribute("Multiplier"))
		RefinedOre:SetAttribute("Size", OreToRefine:GetAttribute("Size"))
		
		RefinedOre:SetAttribute("Player", Player.Name)

				
		RefinedOre.Parent = workspace.ExistingOres
			RefinedOre:MoveTo(RefineryOutput:GetAttribute("Position"))

	end
	
end

local function SellRefinedOre(Player, RefinedBasicOre, Building, Floor)
	local Price = Main:GetClass("OreData")[string.gsub(RefinedBasicOre.Name, "Refined", "")]["Price"]
	Price *= RefinedBasicOre:GetAttribute("Size")
	Price *= RefinedBasicOre:GetAttribute("Multiplier")
	
	for _, ColorTable in pairs(Main:GetClass("ColorData")) do
		if ColorTable[1] == PlayerDataFunction:InvokeServer({"Values", "Floors", nil, "Get"})[Floor]["Color"] then
			Price *= ColorTable[3]
		end
	end
	
	RefinedBasicOre:Destroy()
	if Building.Parent then
	local CashCollector =  Building.Parent.Parent.Parent:WaitForChild("StarterBuildings"):WaitForChild("CashCollector")
	local MoneyText = CashCollector:WaitForChild("MoneyText").Main.TextLabel
	
	CurrentMoney += math.round(Price * 100) / 100 
	
	MoneyText.Text = comma_value(CurrentMoney)
	end
end

function Refinery.Init(Player, Building, Floor)
	
	local Plot = Building.Parent.Parent.Parent.Parent
	
	CurrentMoney = Plot.Stats:GetAttribute("CurrentMoney")
	
	local CashCollector =  Building.Parent.Parent.Parent:WaitForChild("StarterBuildings"):WaitForChild("CashCollector")
	local MoneyText = CashCollector:WaitForChild("MoneyText").Main.TextLabel

	MoneyText.Text = comma_value(CurrentMoney)
	
	if not Refinery[Player] then
		Refinery[Player] = {}
	end
		
	local RefineryInput : BasePart = Building:WaitForChild("RefineryInput")
	local RefineryOutput : BasePart = Building:WaitForChild("RefineryOutput")
	local Control : Model = Building:WaitForChild("Control")
	local ProximityPrompt : ProximityPrompt = Control:WaitForChild("ProximityPromptHolder"):WaitForChild("ProximityPrompt")
		
	ProximityPrompt.Triggered:Connect(function()
		OutputOre(Player, RefineryOutput)
	end)
	
	RefineryInput.Touched:Connect(function(hit)
		if hit:GetAttribute("IsOre") then
			InputOre(Player, hit)
		elseif hit.Parent then
			if hit.Parent:GetAttribute("IsOre") then
				InputOre(Player, hit.Parent)
			end
		end
	end)
	
	local OreSellPoint = Building:WaitForChild("OreSellPoint")
	local SellPoint = OreSellPoint:WaitForChild("SellPoint")
	
	SellPoint.Touched:Connect(function(hit)
		print(hit:GetAttribute("IsRefined"))
		if hit:GetAttribute("IsRefined") then
			SellRefinedOre(Player, hit, Building, Floor)
		elseif hit.Parent then
			if hit.Parent:GetAttribute("IsRefined") then
			SellRefinedOre(Player, hit.Parent, Building, Floor)
			end
		end
	end)
	
	local CashCollector = Building.Parent.Parent.Parent:WaitForChild("StarterBuildings"):WaitForChild("CashCollector")
	local TouchArea = Building.Parent.Parent.Parent:WaitForChild("StarterBuildings"):WaitForChild("CashCollector"):WaitForChild("TouchArea")

	TouchArea.Touched:Connect(function(hit)
		
		
		if hit.Parent == Player.Character then
			
			if not SellDebounce then
				
				SellDebounce = true
				
				if not game:GetService("MarketplaceService"):UserOwnsGamePassAsync(Player.UserId, 1416342595) then
				PlayerDataFunction:InvokeServer({"Values", "Money", CurrentMoney, "Add"})
				else
					PlayerDataFunction:InvokeServer({"Values", "Money", CurrentMoney * 2, "Add"})
				end
				CurrentMoney = 0
				
				task.defer(function()
					task.wait(0.1)
					SellDebounce = false
				end)
								
			end
			
			
		end
	end)
	
end

return Refinery

Fire To Specfic Client and then make part for that Specfic if touched then client send to server data that part is touched and function run

You don’t need overadd parts to server

you can make client-sided parts

add count parts (in server)

But i want all clients to see the part