How to create, destroy, and render 30k+ parts quickly without damaging server preformance?

Im making a farming game where you harvest LOTS of crops. There can be up to 30k crops in a feild at once. Im telling the server to harvest a crop with unreliable remote events, so lots of events are fired quickly. Most of the time, when a crop is collected it is instantly respawned with a new rarity, position, ect. When I regrow them, I just move them and change their values instead of making a whole new instance (to save performance) But still, with just one player the server can lag and get behind massively. Im willing to restructure all of my code to achieve server performance boosts if it is needed
Heres an old version of the game to provide some context: [3 DAYS] Farming Simulator 99 - PUBLIC ALPHA - Roblox

If anyone can provide tips or even ideas on how to boost server performance it would help

2 Likes

What happens when you connect the touched events on the server instead of using a RemoteEvent?

1 Like

Has to be a way to let them build a bit … check what they have and fire less events.
Them mining programs come to mind. You fill the bag you have to move to a spot to get credit for what you got … All that cuts down on server pounding time.

1 Like

I have thought of this, all tools are local based right now, but i might try that soon

1 Like

There is a system called Part Cache used for this sort of thing. Used it for projectiles before. Search the forum for the topic on it.

1 Like

Instead of firing the event 30k times (wich i don’t recommend) you could fire it once with a table containing all the crops, and then on the server you could loop thru them and to prevent lag you could wait(0.01) every for example 100 crops

just got around to implementing this, but the players movement (and therefore the tools position) is so badly delayed between the client and server that it looks laggy and the game becomes unsatisfying and i cant have that happen. It did improve performance, but i dont think its possible to use the clients position for doing touch on the server without remote events. if there is a way it would be greatly helpful, but for now theres no solution

Show me the code you have please.

local module = {}



local rs = game:GetService('ReplicatedStorage')
local events = rs:WaitForChild('events')
local values = rs.globalvalues
local cropsmined = 0

function module.cropmain(newcrop,feild)
	local feilddimensionlist = require(rs.Dependencies.Dependencies)['FeildDimensions'][feild]
	local feildcroplist = require(rs.Dependencies.Dependencies)['CropList'][feild]
	local minx = feilddimensionlist['minx']
	local maxx = feilddimensionlist['maxx']
	local minz = feilddimensionlist['minz']
	local maxz = feilddimensionlist['maxz']
	local crops = values[feild..'Crops']
	local newx = math.random(minx, maxx)
	local newz = math.random(minz, maxz)

	newcrop.Position = Vector3.new(newx, 2.2, newz)
	local rarity
	local raritynum = math.random(1, 100)
	newcrop.Feild.Value = feild
	if raritynum <= 81 then
		newcrop.Type.Value = feildcroplist[1][1]
		newcrop.BrickColor = feildcroplist[1][2]
	elseif raritynum <= 95 then
		newcrop.Type.Value = feildcroplist[2][1]
		newcrop.BrickColor = feildcroplist[2][2]
	elseif raritynum <= 99 then
		newcrop.Type.Value = feildcroplist[3][1]
		newcrop.BrickColor = feildcroplist[3][2]
	elseif raritynum == 100 then
		local randomChance = math.random(1, rs.globalvalues.DiamondRarity.Value)
		if randomChance == 1 then
			newcrop.Type.Value = feildcroplist[4][1]
			newcrop.BrickColor = feildcroplist[4][2]
			if math.random(1,150) == 1 then
				newcrop.Type.Value = feildcroplist[5][1]
				newcrop.BrickColor = feildcroplist[5][2]
				newcrop.Material = Enum.Material.Neon
				for k,v in pairs(game.ReplicatedStorage.spawnables.rare:GetChildren()) do
					v:Clone().Parent = newcrop
				end
			end
		else
			newcrop.Type.Value = feildcroplist[3][1]
			newcrop.BrickColor = feildcroplist[3][2]
		end
	else
		--newcrop.Type.Value = 'wheat' -- Default value in case raritynum is out of range
	end
	newcrop.Parent = workspace
end

function module.harvest(part, multi, player)
	local feild = player.Values.Feild.Value
	local feilddimensionlist = require(rs.Dependencies.Dependencies)['FeildDimensions'][feild]
	local feildcroplist = require(rs.Dependencies.Dependencies)['CropList'][feild]
	local minx = feilddimensionlist['minx']
	local maxx = feilddimensionlist['maxx']
	local minz = feilddimensionlist['minz']
	local maxz = feilddimensionlist['maxz']
	local crops = values[feild..'Crops']
	
	
	
	
	
	
	
	
	
	
	-- harvest main calculations
	if part:FindFirstChild('Type') then

		local amt = 1*multi
		local special = 'None'
		local special2 = ''
		local totalspecial
		if Random.new():NextNumber(1,100) <= player.Values.CritChance.Value then
			amt = player.Values.CritPower.Value*multi
			special = 'Crit'

		end
		if Random.new():NextNumber(1,100) <= player.Values.GoldenChance.Value then
			amt = amt*5
			special2 = 'Golden'

		end
		player.Values[part.Type.Value].Value +=amt
		events.CalculateQuestProgress:Fire(player, part, part.Type.Value, special, special2)
		local charmlist = require(rs.Lists)['Charms']

		if math.random(1,rs.globalvalues.CharmRarity.Value) == 1 then
			local charm = charmlist[math.random(1,#charmlist)]
			game.ReplicatedStorage.events.Notification:FireClient(player, 'charm', charm)
			player.Values.Charms[charm..'1Charms'].Value += 1
			game.ReplicatedStorage.events.NewEnchant:FireClient(player)
			events.CalculateQuestProgress:Fire(player, nil, 'Forage', nil, nil)
		end
		totalspecial = special..special2

		player.PlayerGui.HarvestMarker.Start:FireClient(player, amt, part.Type.Value, part.BrickColor, totalspecial)


	end
	cropsmined += 1
	if (values.InstaGrowback.Value == true or cropsmined/values.Growbackamt.Value == math.floor(cropsmined/values.Growbackamt.Value)) and (part.Type.Value ~= 'lavarock') then
		module.cropmain(part, feild)
	else
		local insprinklerrange = false
		for k,v in pairs(workspace.Sprinklers:GetChildren()) do
			local distance = (part.Position - v.Main.Position).Magnitude
			if distance <= v:GetAttribute('Range') then
				insprinklerrange = true

			end
		end
		if insprinklerrange == true and part.Type.Value ~= 'lavarock' then

			module.cropmain(part,feild)

		else
			part:Destroy()
			crops.Value -= 1
		end

	end
	
	
end

function module.main(tool)
	local lastcollectedcrop = nil
	tool.Equipped:Connect(function()
		--game.ReplicatedStorage.events.CalculatePassives:FireServer()
		player = game.Players[tool.Parent.Name]
		tool:SetNetworkOwner(player)
		tool.hb:SetNetworkOwner(player)
		tool.Handle:SetNetworkOwner(player)
	end)


	tool.hb.Touched:Connect(function(part)
		if part.Name == 'Crop' and part:FindFirstChild('Type') and part ~= lastcollectedcrop then
			lastcollectedcrop = part
			player = game.Players[tool.Parent.Name]
			module.harvest(part, tool.hb.multiplier.Value, player)

		end
	end)
end
return module