Need help with overlapping blocks in cave generation

Hello, so as you some of you know im trying to make a mining game. I am stuck on the caves. Not on the generation mind you, but on the overlapping/“firefighting” blocks. This is so weird and mind boggeling for me because i already have a statement to make this not happen. Sometimes it works, sometimes it does not.

You can write your topic however you want, but you need to answer these questions:

  1. What do you want to achieve? Make the blocks not overlap, EVER

  2. What is the issue? Blocks overlapping when generating caves. Here’s a gif of blocks overlapping only on caves, for some reason: (the big rectangle is a test script found on the bottom of blockhandler)
    Animation - Gifyu (takes a minute to load)

  3. What solutions have you tried so far? Did you look for solutions on the Developer Hub?
    I have litterally spent a day trying to figure out this one bug. I have tried so many things i forgot and can’t even list them here.

With all that out of the way, i know what’s wrong with my script but for some reason i can’t fix it. Here are the lines that are wrong: (Like i said, only caves do this)

if orginPosition and SavedPositions[tostring(addedCFrame)] then return end
SavedPositions[tostring(addedCFrame)] = true

Now another answer might be that the cave generation branches off and that can make overlapping blocks. I really, really don’t know.

I’ll link the place here
Stalagmines.rbxl (269.1 KB)
. I’m looking at work of other people to help me so some unrelated files may show up. The one you’re interested for is blockhandler under ServerScriptService.GameScripts.

And i’d like you to ask to please provide useful answers! Obvious but very bad-performance answers like iterating over every block once every block is super slow. I don’t want to quit a project again.

Right.

The core of your issue with your checks is that numbers are not always equal in the world of floating point numbers. What I mean by this is the following: the reason that your parts are overlapping in some cases are that originPosition is equal to, for example, (1.99999997, 46634, 2) while in the case that causes duplication, it is equal to (2, 46634, 2). These two vectors are not equal to each other. You could resolve this by rounding in this case.

My (assumed) source of your issue after your checks not working is here:

if recursion >= 16000 then
			script.RecursiveBypass:Fire(nil, block) --Bypass for max recursion (20000) (does maybe not work)
		else
			MineOre(nil, block)
		end

After doing the logic in MineOre, you then run this code block:

for x = -1,1 do
		local vector = Vector3.new(x*blockSize,0,0) --Vectors plus position makes the blocks suround the mined block
		GenerateOre(position,vector)
	end
	for y = -1,1 do
		local vector = Vector3.new(0,y*blockSize,0)
		GenerateOre(position,vector)
	end
	for z = -1,1 do
		local vector = Vector3.new(0, 0, z*blockSize)
		GenerateOre(position,vector)
	end

The problem with this as far as I can tell from my view is that you call the function to generate the blocks around the area of the first block. Unless if I am mistaken, it looks like this (in 2d, red is original block, blue are the blocks created in that second block.):

The problem is that by looping in the manner you are above, you are actually returning to the same position that you were before.
The green block represents the block in the second iteration of the recursive code, and the pink blocks are the one created with that iteration of the function.

The creation results in that red block being created a second time. Without the checks in place, this would result in an infinite loop that would result in a crash. However, the float issue mentioned above is consistent, resulting this recursion only running twice, due to these if statements.

	if orginPosition and SavedPositions[tostring(addedCFrame)] then return end
	--if orginPosition and SavedPositions[PositionKey(blockOrginPosition)] then return end
	if addedCFrame.Position.Y >= orgin.Position.Y then return end

If you comment these blocks out - you’ll notice that the resulting behavior is two blocks being created repetitively with no end.

While this gives reasoning for the question at hand, I am recommending that you reconsider the structure that you are using for your project and rewrite your code. There is zero reason that you should be using recursion in this algorithm. You are merely making your code needlessly complex. Assuming I understand your concept correctly, the world can be defined as an area. Simply iterating through this area and deciding what blocks should be made would be a drastically better solution.

OMG tysm!!! i was stuck on this problem and was it really that simple? All i had to do was replace the stringify both on those two lines to this!!

local function PositionKey(vector)
	local vector = vector
	if typeof(vector) == "CFrame" then
		vector = vector.Position
	end
	return math.round(vector.x)..","..math.round(vector.y)..","..math.round(vector.z)
end

(Unaproved post for some reason before i made this one)

Hmmm… i have thought of the floating point number before, although that is one thing i didn’t try to debug… ill try rounding the numbers down.

The reason im using this recursion is because this mine is supposed to be infinite, and while i may use a chunk system like minecraft, i do not quite know how to do that. Maybe you could help me with that?

Anyway, what im looking to recreate/make better is the systems that azure mines and quarry uses. Azure mines uses the same system but without the inbetween MineOre(). Azure mines code is really, really, REALLY messy so that’s why im recreating it instead of using it’s code directly. I also want to add new features.

If you have a set world boundary, simply get the region that you want terrain to generate in and generate inside of that.

If you want to dynamically generate terrain, track where players are going and generate the terrain based on that. Note that this will be resource intensive, and you will want to generate the terrain on the client and replicate it through remotes (as well as loading and unloading on the client,) or else your game will lag, terribly.

There is no world boundary, and the latter can be solved by having streamingenabled on while still using this system. Still, im very, very thankfull for your answer! (Sorry for bad spelling)