How do i optimize server script, what uses heartbeat, connections, what trying to simulate custom physics

optimize server script, what uses heartbeat, connections, what trying to simulate custom physics, for dropping gun,
When i dropping a gun, 1-8 times, everything is okay, but after 14 times, every throw everything gets worse and worse, it starts lagging, and doing strange things, artifacts.

(cause of bad quality theyre looking idk really same i guess)

Perfect throw without lagging:

bad lagging after 9 throwings

a bit still same lagging after 15 throwings (gun can throw th walls after that.)

aslo it already clean ups a bit but it doesnt works right ig

local function cleanupThrow(throwId)
	local throwData = activeThrows[throwId]
	if not throwData then return end

	throwData.running = false

	if throwData.clonedModel and throwData.clonedModel.Parent then
		throwData.clonedModel:Destroy()
	end

	if throwData.tool and throwData.tool.Parent then
		throwData.tool.Parent = throwData.player.Backpack
	end

	activeThrows[throwId] = nil
end


local function startPhysicsLoop()
	if physicsConnection then return end

	local sharedRaycastParams = RaycastParams.new()
	sharedRaycastParams.FilterType = Enum.RaycastFilterType.Blacklist

	physicsConnection = RunService.Heartbeat:Connect(function(deltaTime)
		local currentTime = tick()
		local toRemove = {}

		for throwId, throwData in pairs(activeThrows) do
			if not throwData.running then
				table.insert(toRemove, throwId)
				continue
			end

			if not throwData.grip or not throwData.grip.Parent then
				table.insert(toRemove, throwId)
				continue
			end

			local grip = throwData.grip
			local velocity = throwData.velocity
			local currentPos = throwData.currentPos
			local bounceCount = throwData.bounceCount or 0
			local isGrounded = throwData.isGrounded or false
			local startTime = throwData.startTime

			local elapsed = currentTime - startTime

			if elapsed > 12 then
				table.insert(toRemove, throwId)
				continue
			end

			if isGrounded then continue end

			velocity = velocity + Vector3.new(0, -GRAVITY * deltaTime, 0)
			local newPos = currentPos + (velocity * deltaTime)

			local filterInstances = {grip.Parent}
			if throwData.player.Character then
				table.insert(filterInstances, throwData.player.Character)
			end
			sharedRaycastParams.FilterDescendantsInstances = filterInstances

			local raycastResult = workspace:Raycast(currentPos, (newPos - currentPos), sharedRaycastParams)

			if raycastResult then
				local normal = raycastResult.Normal
				local instance = raycastResult.Instance

				if instance and (instance.Transparency == 1 or not instance.CanCollide or instance:IsA("TrussPart")) then
				else
					local dotProduct = velocity:Dot(normal)

					if dotProduct < 0 then 
						if math.abs(normal.Y) < 0.3 then 
							bounceCount = bounceCount + 1
							if bounceCount <= 4 then
								local reflection = velocity - (2 * dotProduct) * normal
								velocity = reflection * (BOUNCE_DAMPING * (0.8 ^ bounceCount))
								newPos = raycastResult.Position + (normal * 0.2)
							else
								isGrounded = true
								velocity = Vector3.new(0, 0, 0)
								grip.Anchored = true
							end
						elseif normal.Y > 0.7 then 
							if velocity.Magnitude > MIN_BOUNCE_VELOCITY and bounceCount < 3 then
								bounceCount = bounceCount + 1
								local reflection = velocity - (2 * velocity:Dot(normal)) * normal
								velocity = reflection * (BOUNCE_DAMPING * (0.7 ^ bounceCount))
								newPos = raycastResult.Position + Vector3.new(0, 0.1, 0)
							else
								isGrounded = true
								newPos = Vector3.new(newPos.X, raycastResult.Position.Y + 0.1, newPos.Z)
								velocity = Vector3.new(0, 0, 0)
								grip.Anchored = true
							end
						else 
							bounceCount = bounceCount + 1
							if bounceCount <= 3 then
								local reflection = velocity - (2 * dotProduct) * normal
								velocity = reflection * 0.6
								newPos = raycastResult.Position + (normal * 0.15)
							else
								isGrounded = true
								velocity = Vector3.new(0, 0, 0)
								grip.Anchored = true
							end
						end
					end
				end
			end

			currentPos = newPos

			if not isGrounded then
				local spinSpeed = 100
				local currentRotation = CFrame.Angles(
					0, 
					math.rad(spinSpeed * elapsed),
					math.rad(90)
				)
				grip.CFrame = CFrame.new(currentPos) * currentRotation
			else
				grip.CFrame = CFrame.new(currentPos) * CFrame.Angles(0, 0, math.rad(90))
			end

			throwData.velocity = velocity
			throwData.currentPos = currentPos
			throwData.bounceCount = bounceCount
			throwData.isGrounded = isGrounded
			throwData.lastUpdate = currentTime
		end

		for _, throwId in ipairs(toRemove) do
			cleanupThrow(throwId)
		end

		if physicsConnection and next(activeThrows) == nil then
			physicsConnection:Disconnect()
			physicsConnection = nil
		end
	end)
end

edited: i just noticed it looks the same on videos wth

are you using :Disconnect() ?

local function cleanupThrow(throwId, connection, flightPrompt, clonedModel)
	if connection and connection.Connected then
		connection:Disconnect()
	end

	if flightPrompt then
		flightPrompt:Destroy()
	end

	if clonedModel and clonedModel.Parent then
		clonedModel:Destroy()
	end

	activeThrows[throwId] = nil
end

yes i showed it in code

  • and aslo in other code parts

maybe you can try deleting the connection.Connected parts idk

it gives an error " ServerScriptService.acsTOOLdropper:121: attempt to index boolean with ‘Destroy’ "