This is a complex thread, but I really need help.
I don’t expect anyone to rewrite this for me, I just want ideas on how I can optimize it.
My game has been lagging for ages, and I don’t know how to fix it. It doesn’t lag on the client however, it lags on the server. I’m preforming loads of calculations on the server every second, but, there is no other way to do it.
I came here looking for help and if there is anyway I can optimize this.
My game is supposed to be a satisfying farming game, where you collect large amounts of crops instantly and smoothly
Here is footage of my game:
External MediaYou can see I’m collecting a lot, and it looks instant, right? So what’s the problem?
Well it only looks instant on the client, the crop gets teleported away out of view as the server preforms its calculations
I’m sending a list of the crops that were collected in that frame to the server every frame using a remote event, and then having each crop calculated on individually.
But with all of these crops being processed each frame, the server slows down, and has less time to compute other game stuff.
I’m going to share all of my code, I know I shouldn’t but this is important to me.
It’s uncommented, so I apologize, but if you can bear through it and help me solve this I would appreciate it.
keep in mind, sometimes crops will regrow instantly, if that may be an issue
tool code (client) :
local module = {}
local neededparent = workspace.crops
local rs = game:GetService('ReplicatedStorage')
local runservice = game:GetService('RunService')
local grow = require(rs.Dependencies.GrowV2)
local harvest = grow.harvest
local connections = {}
function module.removeconnections()
for _,c in pairs( connections) do
c:Disconnect()
end
connections = {}
end
function module.main(tool)
tool.Equipped:Connect(function()
game.ReplicatedStorage.events.CalculatePassives:FireServer()
end)
local c = runservice.Heartbeat:Connect(function()
rs.events.cropdestoryed:FireServer(tool.hb.CFrame, tool.hb.Size, tool.hb.multiplier.Value)
end)
table.insert(connections, c)
tool.hb.Touched:Connect(function(part)
if part.Parent == neededparent then
part.Position = Vector3.new(-100,-100,-100)
end
end)
end
script that sends client messages to the harvest module:
local rs = game:GetService('ReplicatedStorage')
local neededparent = workspace:WaitForChild('crops')
local grow = require(rs.Dependencies.GrowV2)
local harvest = grow.harvest
rs.events.cropdestoryed.OnServerEvent:Connect(function(plr,cframe,size, multiplier)
local crops = workspace:GetPartBoundsInBox(cframe, size)
for _,crop in pairs(crops) do
if crop.Parent == neededparent and crop.CanBeDestroyed.Value == true then
crop.CanBeDestroyed.Value = false
task.spawn(function()
harvest(plr, crop, multiplier)end)
end
end
end)
main harvest module:
local module = {}
local rs = game:GetService('ReplicatedStorage')
local dependencies = require(rs.Dependencies.Dependencies)
local fieldDimensions = dependencies.FeildDimensions
local croptemplate = rs.spawnables.Crop
local cropcontainer = workspace.crops -- folder to hide when debugging ! :D :D
local cropamtlist = rs.globalvalues.CropAmt
local values = rs.globalvalues
local events = rs.events
local cropinfo = {
['Classic'] = {
{['cropname'] = 'wheat', ['brickcolor'] = BrickColor.new('Parsley green')};
{['cropname'] = 'apple', ['brickcolor'] = BrickColor.new('Bright red')};
{['cropname'] = 'carrot', ['brickcolor'] = BrickColor.new('Neon orange')};
{['cropname'] = 'diamond', ['brickcolor'] = BrickColor.new('Cyan')};
{['cropname'] = 'lavarock', ['brickcolor'] = BrickColor.new('Maroon'), ['material'] = Enum.Material.Neon, ['rareity'] = 150, ['effects'] = rs.spawnables.rare};
{'none'};
['material'] = Enum.Material.Sand;
['height'] = 2.2;
['croplimit'] = 10_000;
};
['Volcanic'] = {
{['cropname'] = 'volcanic wheat', ['brickcolor'] = BrickColor.new('Parsley green')};
{['cropname'] = 'volcanic apple', ['brickcolor'] = BrickColor.new('Bright red')};
{['cropname'] = 'volcanic carrot', ['brickcolor'] = BrickColor.new('Neon orange')};
{['cropname'] = 'black diamond', ['brickcolor'] = BrickColor.new('Really black')};
{['cropname'] = 'lavarock', ['brickcolor'] = BrickColor.new('Maroon'), ['material'] = Enum.Material.Neon, ['rareity'] = 50, ['effects'] = rs.spawnables.rare};
{'none'};
['material'] = Enum.Material.Sand;
['height'] = 2.2;
['croplimit'] = 10_000;
};
['Aquatic'] = {
{['cropname'] = 'aquatic wheat', ['brickcolor'] = BrickColor.new('Parsley green')};
{['cropname'] = 'aquatic apple', ['brickcolor'] = BrickColor.new('Bright red')};
{['cropname'] = 'aquatic carrot', ['brickcolor'] = BrickColor.new('Neon orange')};
{['cropname'] = 'sulfur', ['brickcolor'] = BrickColor.new('New Yeller')};
{['cropname'] = 'bubble', ['brickcolor'] = BrickColor.new('Dark blue'), ['material'] = Enum.Material.Neon, ['rareity'] = 50, ['effects'] = rs.spawnables.bubblerare};
{'none'};
['material'] = Enum.Material.Sand;
['height'] = 2.2;
['croplimit'] = 10_000;
};
['Planetoid'] = {
{['cropname'] = 'cosmic wheat', ['brickcolor'] = BrickColor.new('Parsley green')};
{['cropname'] = 'cosmic apple', ['brickcolor'] = BrickColor.new('Bright red')};
{['cropname'] = 'cosmic carrot', ['brickcolor'] = BrickColor.new('Neon orange')};
{['cropname'] = 'cosmic diamond', ['brickcolor'] = BrickColor.new('Eggplant')};
{['cropname'] = 'moondust', ['brickcolor'] = BrickColor.new('Really black'), ['material'] = Enum.Material.Neon, ['rareity'] = 50, ['effects'] = rs.spawnables.moondustrare};
{'none'};
['material'] = Enum.Material.Sand;
['height'] = 2.2;
['croplimit'] = 30_000;
};
['Glacier'] = {
{['cropname'] = 'icy wheat', ['brickcolor'] = BrickColor.new('Parsley green')};
{['cropname'] = 'icy apple', ['brickcolor'] = BrickColor.new('Bright red')};
{['cropname'] = 'icy carrot', ['brickcolor'] = BrickColor.new('Neon orange')};
{['cropname'] = 'icy diamond', ['brickcolor'] = BrickColor.new('Cyan')};
{['cropname'] = 'icicle', ['brickcolor'] = BrickColor.new('Navy blue'), ['material'] = Enum.Material.Glacier, ['rareity'] = 50, ['effects'] = rs.spawnables.iciclerare};
{'none'};
['material'] = Enum.Material.Glacier;
['height'] = 2.2;
['croplimit'] = 30_000;
};
['Desert'] = {
{['cropname'] = 'deserted wheat', ['brickcolor'] = BrickColor.new('Parsley green')};
{['cropname'] = 'deserted apple', ['brickcolor'] = BrickColor.new('Bright red')};
{['cropname'] = 'deserted carrot', ['brickcolor'] = BrickColor.new('Neon orange')};
{['cropname'] = 'sand', ['brickcolor'] = BrickColor.new('Bright orange')};
{['cropname'] = 'glass', ['brickcolor'] = BrickColor.new('Institutional white'), ['material'] = Enum.Material.Glass, ['rareity'] = 50, ['effects'] = rs.spawnables.nonerare};
{'none'};
['material'] = Enum.Material.Sand;
['height'] = 2.2;
['croplimit'] = 30_000;
};
['Neon'] = {
{['cropname'] = 'neon wheat', ['brickcolor'] = BrickColor.new('Parsley green')};
{['cropname'] = 'neon apple', ['brickcolor'] = BrickColor.new('Bright red')};
{['cropname'] = 'neon carrot', ['brickcolor'] = BrickColor.new('Neon orange')};
{['cropname'] = 'neon diamond', ['brickcolor'] = BrickColor.new('Steel blue')};
{['cropname'] = 'pink crystal', ['brickcolor'] = BrickColor.new('Carnation pink'), ['material'] = Enum.Material.Neon, ['rareity'] = 50, ['effects'] = rs.spawnables.pinkrare};
{['cropname'] = 'purple crystal', ['brickcolor'] = BrickColor.new('Royal purple'), ['material'] = Enum.Material.Neon, ['rareity'] = 200, ['effects'] = rs.spawnables.purplerare};
['material'] = Enum.Material.Neon;
['height'] = 247.697;
['croplimit'] = 100_000;
};
}
function module.grow(field, crop)
if cropamtlist[field].Value < cropinfo[field].croplimit then
local newx = math.random(fieldDimensions[field].minx, fieldDimensions[field].maxx)
local newz = math.random(fieldDimensions[field].minz, fieldDimensions[field].maxz)
crop.CanBeDestroyed.Value = true
crop.Position = Vector3.new(newx, cropinfo[field].height, newz)
crop.Material = cropinfo[field].material
local rarity
local raritynum = math.random(1, 100)
crop.Feild.Value = field
if raritynum <= 81 then
crop.Type.Value = cropinfo[field][1].cropname
crop.BrickColor = cropinfo[field][1].brickcolor
elseif raritynum <= 95 then
crop.Type.Value = cropinfo[field][2].cropname
crop.BrickColor = cropinfo[field][2].brickcolor
elseif raritynum <= 99 then
crop.Type.Value = cropinfo[field][3].cropname
crop.BrickColor = cropinfo[field][3].brickcolor
elseif raritynum == 100 then
local randomChance = math.random(1, rs.globalvalues.DiamondRarity.Value)
if randomChance == 1 then
crop.Type.Value = cropinfo[field][4].cropname
crop.BrickColor = cropinfo[field][4].brickcolor
if math.random(1,cropinfo[field][5].rareity) == 1 then
if cropinfo[field][6][1] ~= 'none' then -- if 6th crop
if math.random(1,cropinfo[field][6].rareity) == 1 then
crop.Type.Value = cropinfo[field][6].cropname
crop.BrickColor = cropinfo[field][6].brickcolor
crop.Material = cropinfo[field][6].material
for k,v in pairs(cropinfo[field][6].effects:GetChildren()) do
v:Clone().Parent = crop
end
end
else --5th crop
crop.Type.Value = cropinfo[field][5].cropname
crop.BrickColor = cropinfo[field][5].brickcolor
crop.Material = cropinfo[field][5].material
for k,v in pairs(cropinfo[field][5].effects:GetChildren()) do
v:Clone().Parent = crop
end
end
end
else
crop.Type.Value = cropinfo[field][3].cropname
crop.BrickColor = cropinfo[field][3].brickcolor
end
end
crop.Parent = workspace.crops
else
crop:Destroy() -- remove from memory, it wasnt used
end
end
function module.harvest(player, part, multi)
local field = player.Values.Feild.Value
local insprinklerrange = false
local sprinkmulti = 1
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
sprinkmulti += v:GetAttribute('Multiplier')
end
end
local amt = 1*multi
local special = 'None'
local special2 = ''
local special3 = ''
local totalspecial
if Random.new():NextNumber(0.1,100) <= player.Values.CritChance.Value then
amt = player.Values.CritPower.Value*multi
special = 'Crit'
end
if Random.new():NextNumber(0.1,100) <= player.Values.GoldenChance.Value + values.WeatherGolden.Value then
amt = amt*5
special2 = 'Golden'
end
if Random.new():NextNumber(0.1,100) <= player.Values.RainbowChance.Value + values.WeatherRainbow.Value then
amt = amt*10
special3 = 'Rainbow'
end
-- feildmulti, final amt calculations
amt = amt * player.Values.FeildMultis[field].Value * sprinkmulti * player.Values.FriendMulti.Value
player.Values[part.Type.Value].Value +=amt
events.CalculateQuestProgress:Fire(player, part, part.Type.Value, special, special2)
events.CalculateQuestProgress:Fire(player, part, part.Type.Value, 'Collect', amt)
local charmlist = require(rs.Lists)['Charms']
local itemlist = require(rs.Lists)['Items']
local charmforagechance = rs.globalvalues.CharmRarity.Value - player.Values.CharmChance.Value
-- max charmforage change (w/o gamepass)
if charmforagechance < 500 then
charmforagechance = 500
end
local foragechance = player.Values.ForageChance.Value
if math.random(1,charmforagechance/foragechance) == 1 then
local charm = charmlist[math.random(1,#charmlist)]
game.ReplicatedStorage.events.Notification:FireClient(player, 'You found a '.. charm.. ' charm!')
player.Values.Charms[charm..'1Charms'].Value += 1
rs.events.InventoryNotif:FireClient(player, 'Charm')
game.ReplicatedStorage.events.NewEnchant:FireClient(player)
events.CalculateQuestProgress:Fire(player, nil, 'Forage', nil, nil)
end
local itemlist = require(rs.Lists)['Items']
if math.random(1,50_000/foragechance) == 1 then
local item = itemlist[math.random(1,#itemlist)]
game.ReplicatedStorage.events.Notification:FireClient(player, 'You found a '..item.. '!')
player.Values.Items[item].Value += 1
game.ReplicatedStorage.events.NewItem:FireClient(player)
events.CalculateQuestProgress:Fire(player, nil, 'Find', nil, nil)
end
totalspecial = special..special2..special3
player.PlayerGui.HarvestMarker.Start:FireClient(player, amt, part.Type.Value, part.BrickColor, totalspecial)
if (values.InstaGrowback.Value or insprinklerrange) and part:FindFirstChild('effect') == nil then
module.grow(field,part)
else
part:Destroy()
cropamtlist[field].Value -= 1
end
end
return module
I know this is a lot, but I can’t publish a laggy game and I have no solutions.
I am open to answering any questions, if you have any tell me!