How to make a less laggy chunk system?

I am working on a chunk system for my mining game but it causes lag. I only have certain events for adding blocks like when mining a block or the mine is made. It creates the first 50 layers of blocks above you and first 50 under you. It works fine but like I said, it causes lag. I have a script that’s deleting any blocks that are not in that range but its not helping.

Script:

--Load Chunk
			local PlayerDepth = math.floor(Humanoid.Torso.CFrame.Y)
			for _,BlockData in ipairs(MineData) do
				--Check Position
				if PlayerDepth - (ChunkSize*BlockSize)/2 > BlockData.BlockPosition.Y	then
					break
				end	
				--Top
				if BlockData.BlockPosition.Y - PlayerDepth <= (ChunkSize*BlockSize)/2 and BlockData.BlockPosition.Y - PlayerDepth > -1 then
					--Check if block exists
					local BlockExists = false
					for _,CurrentBlock in pairs(Mine:GetChildren()) do
						if CurrentBlock.Position == BlockData.BlockPosition then
							BlockExists = true
							break
						end
					end
					if not BlockExists then
						local NewBlock = BlockData.BlockType:Clone()
						NewBlock.Position = BlockData.BlockPosition
						NewBlock.Parent = Mine
						wait()
					end
				
					--Bottom	
				elseif PlayerDepth - BlockData.BlockPosition.Y <= (ChunkSize*BlockSize)/2 and PlayerDepth - BlockData.BlockPosition.Y > -1 then
					--Check if block exists
					local BlockExists = false
					for _,CurrentBlock in pairs(Mine:GetChildren()) do
						if CurrentBlock.Position == BlockData.BlockPosition then
							BlockExists = true
							break
						end
					end
					if not BlockExists then
						local NewBlock = BlockData.BlockType:Clone()
						NewBlock.Position = BlockData.BlockPosition
						NewBlock.Parent = Mine
					end
				end
			end

One method I’ve seen that works wonders (it was even used in pokemon brick bronze) is to split your map up into various chunks and have doorways to each chunk. It’s a pretty simple system. When a player touches, clicks, etc. a doorway, load the next chunk, teleport the player to wherever you want them to exit to in the next chunk, and store the previous chunk in ReplicatedStorage or wherever you want besides Workspace. Make sure this is all done on a LocalScript.

Wouldnt that allow exploiters to exploit blocks?

The way I think Brick Bronze did it to prevent that was: they stored the chunks in ServerStorage and whenever they needed to load a new chunk, had a RemoteEvent fire to a ServerScript and the ServerScript would move the chunk into ReplicatedStorage and then the LocalScript would handle the rest. That would probably prevent exploiters from messing with the chunks.

EDIT: I know for a fact that they stored the chunks in ServerStorage, but I do not know if that’s how they handled the chunk loading, that’s just how I would do it.

Can you move blocks from top to bottom or bottom to top as the player moves (rather than deleting them)? An idea might be to start with a set number of blocks and as the player moves up, for example, the blocks on the bottom (that are supposed to be removed) get repurposed by moving them to the top (changing texture/position/whatever). Make sense? Could have a function to add a certain number of extra blocks at once if needed for whatever reason.

I cant move the blocks. There is a table with the preloaded data for what needs loaded and it just retrieves the data. Even with a stepped:wait() its laggy when making a layer. And when using ur method its still laggy

What does a BlockData record look like and how many are in MineData? Is that a table for every block at every position in the world?

BlockData is {BlockType = Block, BlockPosition = Vector3.new(Xpos,Ypos,Zpos)} and there is a couple thousand different values in the table, although the loop breaks once it gets to the last block

The code you posted at the top is run inside stepped?

i tried stepped, heartbeat, and a while loop

Current:

function CheckBlocks()
	while ServerStorage.Mining.Value == true do
		RunService.Stepped:wait()
	end
end

I tried using events too still caused lag.

So that loop looks at all couple of thousand entries in the MineData table looking for individual bricks to move. Since the bricks are presumably in layers (groups of bricks at same Y position), you might reorganize the MineData table so it looks at layers instead of individual bricks and then just checks all bricks at a given layer.

Ill give it a go and see what happens

since the top and bottom move together… you’d only need to check one of those. If you have a pointer into your table that tells where the current “top” is, then you can see if it needs to move and move to the bottom. Then check the next record (row) and if it doesn’t need to move then you are done… break out of the loop. Otherwise, walk through the table until the Y pos offset is what you are looking for.

1 Like

it lags even when at the top of the map which is where the table starts. so wouldnt that do nothing?

Currently it looks like it checks every single record in the MineData on every loop of stepped (or whatever) even if no blocks are moving. That may be enough to cause a problem without any blocks having to change.

It doesn’t really make any sense for code running on any of these events to have a wait() statement inside it. Can you provide more context for the code?

Also, it’s necessary to clarify what you mean by “lag”, because it’s a way-overloaded word on these forums. Unqualified, it normally means internet latency, i.e. experiencing the client-server-client round trip time (ping) as delay between doing something and seeing the server replicate back the result of your actions. But other people use it to mean low render frame rate, or pauses (hitches, freezes) in rendering while something is being computed, or things updating slowing in world due to bottlenecked replication (none of which are really best described as lag). When in doubt, post a video showing the problem.

Also, looking at the microprofiler is a good first step to diagnosing the problem.

1 Like

Shouldn’t need to check that often if it’s tied to player movement. Maybe use an event that fires when the player position changes… or check periodically with a longer delay in a while loop. Again, that check should not have to look at every record. Use a row index and then calculate an offset from that index for every block in the row.

-- thousands of entries
MineData = {
{ {block1data}, {block2data}, ... , {blockndata} },   -- first row of world
{ {block1data}, {block2data}, ... , {blockndata} },   -- second row of world
...
} 

local topPostion = 1    -- currently the top brick

-- dozens of entries
local Bricks = {
{ brick, brick, brick, ...},       -- the bottom row of bricks
{ brick, brick, brick, ...},       -- the top row of bricks
{ brick, brick, brick, ...},
}
 --The index into the bricks table is some % (modulo) of the first table

For example, if the MineData table has 2000 entries, and we are at the top of the world, then index of the top record will be 1 and the index of the “top brick” in the bricks table can be found using that same index and modulo (%).

Each time you check, you look at the Y pos of the first record in MineData[currentIndex] to see if a change is needed. If it is, you add 1 (or subtract if moving up) to the currentIndex and then apply position data from MineData[newIndex] to all the bricks in Bricks[newIndex%#Bricks]…

Something to that effect. Modulo puts 0s when the numbers loop so some modifications will be needed… and you may need to handle going up differently than going down… But basically the core idea is there… You should never have to look at more than 1 entry if the stack doesn’t need to change and not more than 1 row if it does (although you can have it walk through the table during a single loop iteration changing multiple rows until the correct ratio is achieved if the player is falling for example and moving faster than usual).

That is probably the only way I see fixing it, I am not sure what you can do if it does not work.

How is this? I will have to calculate the Min and Max, but this will only get the blocks in that area.

--Update Chunks
					local PlayerDepth = math.floor(Humanoid.Torso.CFrame.Y)
					local Min = 1
					local Max = 2
					
					--Place Block
					for BlockNumber = Min,Max do
						local BlockData = MineData[BlockNumber]
						
						--Check if block exists already
						local Exists = false
						for _,BlockInMine in pairs(Mine:GetChildren()) do
							if BlockInMine.Position == BlockData.BlockPosition then
								Exists = true
								break
							end
						end
						
						--Place Block
						if not Exists then
							local NewBlock = BlockData.BlockType:Clone()
							NewBlock.Position = BlockData.BlockPosition
							NewBlock.Parent = Mine
						end
					end

This wouldn´t be entirely necessary but it would still work. So yeah I suppose it should work as intended.