Hello,
I am working on a project which is essentially a mining game. The goal is to make a system where I can generate a volume of blocks at any size necessary, while having each one be mine-able.
On the smaller side, I’m usually working with 15x15x300 sizes, so even a smaller generation involves ~60000 parts to be generated and regenerated during the lifetime of any particular server. I handle all of the part coordinates/properties/mine-makeup prior to physical generation via tables, so the process of designing the mine works smoothly.
The issue I am running into is that whenever I need to generate parts (When the server starts it usually loads quickly, which is good, but if I need to clear out the mine and regenerate it there is always an issue) I encounter over 20 seconds of lag, sometimes much more depending on the size of the mine. During this period of lag the player is completely unable to talk in chat, respawn, or interact with other things such as tools.
While troubleshooting this problem I’ve tried various methods to lessen the severity of the lag, or even fix it all together if possible. While I’ve had small success with some methods, nothing I’ve tried has made this a playable experience.
The part of the process where I believe is causing this lag is when I need to clone objects and move them in to the workspace. Because I need the mines to be accessible by every player, I am doing this on the server side. I’ve tried making all the parts invisible and without collisions, but this does not help the lag whatsoever. The most recent version of my generation function looks like this:
function GenerationService:generate()
--One big issue I've had with this process is running into the Exhausted Allowed Execution Time
--Error. This happens whenever I need to move the objects to the workspace, so to mitigate this I
--am breaking the volume into separate partitions, and then loading them individually when I need to
--move the mine to the workspace.
local vol = self.length*self.width*self.depth
local partitions = math.floor(vol/50000)
self.cache = {}
for index = 1, partitions + 1, 1 do
table.insert(self.cache, Instance.new("Folder"))
end
--CFrame variables
local lv = self.Origin.CFrame.LookVector
local rv = self.Origin.CFrame.RightVector
local uv = self.Origin.CFrame.UpVector
local partition_index = 1
for index, object in pairs(self.Mine) do
--Every 50,000 blocks, I make a new partition
if index % 50001 == 0 then
partition_index += 1
end
local base = self.Library:FindFirstChild(object.bType)
local new_object = base:Clone()
new_object:SetPrimaryPartCFrame(self.Origin.CFrame + (lv * object.y * 6) - (rv * object.x * 6) - (uv * object.z * 6))
new_object.Parent = self.cache[partition_index]
end
for index, object in pairs(self.cache) do
object.Parent = self.Parent
--Despite making the separate partitions... If I don't yield here I still run into
--the execution time error.
task.wait()
end
self.Origin:Destroy()
print("Passed Destroyed")
end
The exhausted execution time error has been a big problem working through this process. Sometimes I want to work with hundreds of thousands of parts, at these scales the script will crash because of this error. I’ve tried working in yields every 100,000 parts or so, but this causes the mine to generate at an incredibly, unreasonably slow pace, during which the game still experiences the same amount of unplayable lag.
I’ve tested this process out with and without streaming enabled. While it helps slightly (1-2 seconds improvement), it does not affect the lag and is still unplayable even if the player is very far away.
I’ve thought of incorporating coroutines, but I am not exactly sure if they would even help with this issue or how to implement them. Perhaps I can use them when I need to parent the partition folders, avoiding the yield entirely?
This is what the script calling the generation functions looks like:
local new_mine = Mine.new(mine_origin, grid_allocation, BT)
print("Starting")
new_mine:define(15, 15, 300, "Stone")
new_mine:addLayer(50, 99, "2nd Layer")
new_mine:addLayer(100, 150, "3rd Layer")
new_mine:addLayer(0, 299, "Iron Layer")
new_mine:setLayer("2nd Layer", "HardStone")
new_mine:setLayer("3rd Layer", "Bedrock")
new_mine:populateLayer("Iron Layer", "IronOre", .05)
new_mine:generate()
print("Loaded")
And here is the function I call when I try to regenerate the mine:
local function resetMine()
print("Resetting")
--Message is a BillboardGui that says that the mine is resetting
message.Enabled = true
wait(0.5)
--:clear() wipes the table that holds all the mine information
--and calls :Destroy() on the partitions holding the objects
new_mine:clear()
new_mine:define(15, 15, 300, "Stone")
new_mine:addLayer(50, 99, "2nd Layer")
new_mine:addLayer(100, 150, "3rd Layer")
new_mine:addLayer(0, 299, "Iron Layer")
new_mine:setLayer("2nd Layer", "HardStone")
new_mine:setLayer("3rd Layer", "Bedrock")
new_mine:populateLayer("Iron Layer", "IronOre", .05)
new_mine:generate()
wait(0.5)
message.Enabled = false
end
The generation functions are in a module script and I am calling them with a script in the workspace.
Like previously mentioned, from all my testing the functions other than generate work completely fine and cause no lag, so I only included the generate function in this post, but if there is reason to believe that they could be affecting the script then I can post them too.
I’m sorry for the longer post,
Any help at this point would be greatly appreciated.