Generated blocks' coordinate doesn't register/read correctly

I’m currently working solo on a mining simulator game. Everything went smooth in the development at first until I encountered this problem. I was not able to solve this problem for over 3 days and it have been denying me to have any further development.

The issue that I’m having can be quite complicated. I’ll try to provide as much information as possible and explain it as best as I can.

So, when a player mines a block. New blocks will be generated in all directions. Their coord will be logged so generated coordinates won’t be regenerated. However, there is something wrong with the register process or the reading process. Allowing generated coordinates to be regenerated. I can’t find anything wrong in my script.

Here’s my script. (Only the related portions)

local mine = workspace:WaitForChild("Mine")
local layerModule = require(rep.Modules:WaitForChild("LayerModule"))
local oreModule = require(rep.Modules:WaitForChild("OreModule"))
local generatedBlocks = {Vector3.new(0, 0, 0), Vector3.new(0, 1, 0)}

local function checkOrePosAvailability(orePos) -- see if a position is already generated or not
    local bool
    for i,v in pairs(generatedBlocks) do
	    if orePos == v then
		    bool = false
		    break
		else bool = true
	    end
	end
	print(bool)
	if (orePos.X > 24 or orePos.X < 0 or orePos.Z > 24 or orePos.Z < 24) and orePos.Y == -25 then
		bool = "bedrock"
	end 
	return bool
end

local function getMaxBlock() -- for remote function
	return maximumBlocks
end

local function generateBlockAtPos(orePos, oldOre, moveVector)
	local blockLayer, layerInfo = layerModule.findLayer(orePos.Y)
	local randomOreForLayer = oreModule.getRandomOreForLayer(blockLayer)
	local clonedBlock = serverStorage.Blocks:FindFirstChild(randomOreForLayer):Clone()
	local newOrePosValue = Instance.new("Vector3Value")
	newOrePosValue.Name = "OrePos"
	newOrePosValue.Value = orePos + moveVector
	newOrePosValue.Parent = clonedBlock
	local newOreTypeValue = Instance.new("StringValue")
	newOreTypeValue.Name = "OreType"
	newOreTypeValue.Value = clonedBlock.Name
	newOreTypeValue.Parent = clonedBlock
	clonedBlock.Position = oldOre.Position + (moveVector * oldOre.Size.X)
	clonedBlock.Color = layerInfo.color
	table.insert(generatedBlocks, #generatedBlocks + 1, newOrePosValue.Value)
	clonedBlock.Parent = mine
end

local function generateBedrockAtPos(orePos, oldOre, moveVector)
	local clonedBlock = serverStorage.Blocks:FindFirstChild("Bedrock"):Clone()
	local newOrePosValue = Instance.new("Vector3Value")
	newOrePosValue.Name = "OrePos"
	newOrePosValue.Value = orePos + moveVector
	newOrePosValue.Parent = clonedBlock
	local newOreTypeValue = Instance.new("StringValue")
	newOreTypeValue.Name = "OreType"
	newOreTypeValue.Value = clonedBlock.Name
	newOreTypeValue.Parent = clonedBlock
	clonedBlock.Position = oldOre.Position + (moveVector * oldOre.Size.X)
	table.insert(generatedBlocks, #generatedBlocks + 1, newOrePosValue.Value)
	clonedBlock.Parent = mine
end

--Events
mineOreEvent.OnServerEvent:Connect(function(plr, ore) -- a player mined an ore
	local oreType = ore:FindFirstChild("OreType").Value
	local orePos = ore:FindFirstChild("OrePos").Value
	local newPos = orePos
	-- generate new blocks
	-- generate +X block
	newPos = orePos + Vector3.new(1, 0, 0)
	local PXAvailability = checkOrePosAvailability(newPos)
	print(PXAvailability, newPos)
    if PXAvailability == true then
	    generateBlockAtPos(newPos, ore, Vector3.new(1, 0, 0))
    elseif PXAvailability == "bedrock" then
	    generateBedrockAtPos(newPos, ore, Vector3.new(1, 0, 0))
    end
    -- generate -X block
	newPos = orePos - Vector3.new(1, 0, 0)
    local MXAvailability = checkOrePosAvailability(newPos)
    print(MXAvailability, newPos)
    if MXAvailability == true then
	    generateBlockAtPos(newPos, ore, Vector3.new(-1, 0, 0))
    elseif MXAvailability == "bedrock" then
	    generateBedrockAtPos(newPos, ore, Vector3.new(-1, 0, 0))
    end
    -- generate +Y block
	newPos = orePos + Vector3.new(0, 1, 0)
    local PYAvailability = checkOrePosAvailability(newPos)
    print(PYAvailability, newPos)
    if PYAvailability == true then
	    generateBlockAtPos(newPos, ore, Vector3.new(0, 1, 0))
    elseif PYAvailability == "bedrock" then
	    generateBedrockAtPos(newPos, ore, Vector3.new(0, 1, 0))
    end
    -- generate -Y block
	newPos = orePos - Vector3.new(0, 1, 0)
    local MYAvailability = checkOrePosAvailability(newPos)
    print(MYAvailability, newPos)
    if MYAvailability == true then
	    generateBlockAtPos(newPos, ore, Vector3.new(0, -1, 0))
    elseif MYAvailability == "bedrock" then
	    generateBedrockAtPos(newPos, ore, Vector3.new(0, -1, 0))
    end
    -- generate +Z block
	newPos = orePos + Vector3.new(0, 0, 1)
    local PZAvailability = checkOrePosAvailability(newPos)
    print(PZAvailability, newPos)
    if PZAvailability == true then
	    generateBlockAtPos(newPos, ore, Vector3.new(0, 0, 1))
    elseif PZAvailability == "bedrock" then
	    generateBedrockAtPos(newPos, ore, Vector3.new(0, 0, 1))
    end
    -- generate -Z block
	newPos = orePos - Vector3.new(0, 0, 1)
    local MZAvailability = checkOrePosAvailability(newPos)
    print(MZAvailability, newPos)
    if MZAvailability == true then
	    generateBlockAtPos(newPos, ore, Vector3.new(0, 0, -1))
    elseif MZAvailability == "bedrock" then
	    generateBedrockAtPos(newPos, ore, Vector3.new(0, 0, -1))
    end
    ore:Destroy()
end)

OreModule. (Select a random ore for a layer)

local module = {}

math.randomseed(tick())

local Ores = {
	--Layer 1 (10/100)
	["Stone"] = {mineTime = 3, rankRequired = 1, Gold = 1, Premiumite = 0, Exp = 1, Layer = "all", Rarity = 400},
	["Copper"] = {mineTime = 6, rankRequired = 1, Gold = 16, Premiumite = 0, Exp = 10, Layer = 1, Rarity = 30},
	["Iron"] = {mineTime = 10, rankRequired = 2, Gold = 30, Premiumite = 0, Exp = 20, Layer = 1, Rarity = 10},
	["Coal"] = {mineTime = 5, rankRequired = 1, Gold = 10, Premiumite = 0, Exp = 5, Layer = 1, Rarity = 40},
	["Zinc"] = {mineTime = 8, rankRequired = 2, Gold = 22, Premiumite = 0, Exp = 15, Layer = 1, Rarity = 20},
	--Layer 2 (8/100)
	["Silver"] = {mineTime = 20, rankRequired = 3, Gold = 50, Premiumite = 0, Exp = 50, Layer = 2, Rarity = 40},
	["Lead"] = {mineTime = 20, rankRequired = 3, Gold = 80, Premiumite = 0, Exp = 75, Layer = 2, Rarity = 30},
	["Gold"] = {mineTime = 25, rankRequired = 4, Gold = 120, Premiumite = 0, Exp = 100, Layer = 2, Rarity = 10},
	--Layer 3 (8/100)
	["Common Chest"] = {mineTime = 5, rankRequired = 1, Gold = 1000, Premiumite = 0, Exp = 0, Layer = 3, Rarity = 5},
	["Ruby"] = {mineTime = 30, rankRequired = 5, Gold = 200, Premiumite = 0, Exp = 200, Layer = 3, Rarity = 45},
	["Sapphire"] = {mineTime = 30, rankRequired = 5, Gold = 250, Premiumite = 0, Exp = 250, Layer = 3, Rarity = 30},
	--Layer 4 (8/100)
	["Crystal"] = {mineTime = 30, rankRequired = 6, Gold = 350, Premiumite = 0, Exp = 350, Layer = 4, Rarity = 50},
	["Diamond"] = {mineTime = 35, rankRequired = 7, Gold = 500, Premiumite = 0, Exp = 500, Layer = 4, Rarity = 20},
	["Emerald"] = {mineTime = 40, rankRequired = 7, Gold = 750, Premiumite = 0, Exp = 750, Layer = 4, Rarity = 10},
	["Rare Chest"] = {mineTime = 5, rankRequired = 1, Gold = 2500, Premiumite = 0, Exp = 0, Layer = 4, Rarity = 2},
	--Layer 5 (8/100)
	["Topaz"] = {mineTime = 40, rankRequired = 7, Gold = 1000, Premiumite = 0, Exp = 1000, Layer = 5, Rarity = 40},
	["Amethyst"] = {mineTime = 50, rankRequired = 8, Gold = 5000, Premiumite = 0, Exp = 5000, Layer = 5, Rarity = 4},
	["Platinum"] = {mineTime = 45, rankRequired = 7, Gold = 1500, Premiumite = 0, Exp = 1500, Layer = 5, Rarity = 12},
	["Uranium"] = {mineTime = 45, rankRequired = 8, Gold = 2500, Premiumite = 0, Exp = 2500, Layer = 5, Rarity = 10},
	["Plutonium"] = {mineTime = 45, rankRequired = 7, Gold = 2000, Premiumite = 0, Exp = 2000, Layer = 5, Rarity = 10},
	["Epic Chest"] = {mineTime = 5, rankRequired = 1, Gold = 10000, Premiumite = 0, Exp = 0, Layer = 5, Rarity = 2},
	--Layer 6 (8/100)
	["Garnet"] = {mineTime = 50, rankRequired = 8, Gold = 5000, Premiumite = 0, Exp = 5000, Layer = 6, Rarity = 40},
	["Rainbowium"] = {mineTime = 50, rankRequired = 8, Gold = 10000, Premiumite = 0, Exp = 10000, Layer = 6, Rarity = 10},
	["Opal"] = {mineTime = 50, rankRequired = 8, Gold = 7500, Premiumite = 0, Exp = 7500, Layer = 6, Rarity = 25},
	["Mithril"] = {mineTime = 55, rankRequired = 9, Gold = 25000, Premiumite = 0, Exp = 25000, Layer = 6, Rarity = 4},
	["Rainbow Chest"] = {mineTime = 50, rankRequired = 8, Gold = 100000, Premiumite = 0, Exp = 0, Layer = 6, Rarity = 1},
	--Layer 7 (8/100)
	["Painite"] = {mineTime = 55, rankRequired = 10, Gold = 10000, Premiumite = 0, Exp = 25000, Layer = 7, Rarity = 50},
	["Demonite"] = {mineTime = 55, rankRequired = 10, Gold = 25000, Premiumite = 0, Exp = 10000, Layer = 7, Rarity = 15},
	["Draconium"] = {mineTime = 55, rankRequired = 10, Gold = 30000, Premiumite = 0, Exp = 9000, Layer = 7, Rarity = 10},
	["Ambrosia"] = {mineTime = 60, rankRequired = 11, Gold = 50000, Premiumite = 0, Exp = 50000, Layer = 7, Rarity = 4},
	["Legendary Chest"] = {mineTime = 5, rankRequired = 1, Gold = 250000, Premiumite = 0, Exp = 0, Layer = 7, Rarity = 1},
	
	--Misc
	["Premiumite"] = {mineTime = 15, rankRequired = 1, Gold = 0, Premiumite = 1, Exp = 0, Layer = "all", Rarity = 1},
	["Bedrock"] = {mineTime = 999999999, rankRequired = 999, Gold = 0, Premiumite = 0, Exp = 0, Layer = "all", Rarity = nil},
	["God Rock"] = {mineTime = 120, rankRequired = 1, Gold = 10000000, Premiumite = 0, Exp = 500000, Layer = "all", Rarity = nil}, -- 1/1000000
}

function module.getOreInfo(arg)
	local info = nil
	for i,v in pairs(Ores) do
		if i == arg then
			info = v
		end
	end
	return info
end

function module.getRandomOreForLayer(layer)
	--local allOresOfTheLayer = {}
	print("Layer = " ..layer)
	local tableOfPossibility = {}
	for oreName,v in pairs(Ores) do
		if v.Layer == "all" or v.Layer == layer then
			--[[
			--Rarity converting
			if v.Rarity < 1 then
				local reversedInt = tonumber(string.reverse(v.Rarity)) -- make it into an integer
				local amountOf0 = 1
				local searchingPos = 1
				for i = 1, string.len(reversedInt) do
					if tonumber(string.sub(reversedInt, searchingPos, searchingPos)) == 0 then
						amountOf0 = amountOf0 + 1
					end
					searchingPos = searchingPos + 1
				end
				local factorMaster = math.pow(100, amountOf0 - 1)
				print(reversedInt, factorMaster)
			end
			--]]
			if v.Rarity ~= nil then
			    for i = 1,v.Rarity do
				    table.insert(tableOfPossibility, oreName)
			    end
			elseif oreName == "God Rock" then
				local luckyRandomNumber = math.random(1, 1000000)
				if luckyRandomNumber == 666666 then
					for i = 1,#tableOfPossibility do
						table.remove(tableOfPossibility, 1)
					end
					table.insert(tableOfPossibility, oreName)
					break
				end
			end
		end
	end
	--[[
    local rarestOre
    -- get the lowest possibility
    for i,v in pairs(allOresOfTheLayer) do
	    if rarestOre == nil or v < rarestOre then
		    rarestOre = v
	    end
    end
    -- math stuff that i dont even know how to explain
    local rate = math.ceil(1 / rarestOre)
	--]]
	local randomSelectedOre = tableOfPossibility[math.random(1, #tableOfPossibility)]
	print("random selected ore = " ..randomSelectedOre)
	return randomSelectedOre
end

return module

LayerModule. (Find layer using depth)

local module = {}

local rep = game:GetService("ReplicatedStorage")

local Layers = {
	[1] = {color = Color3.fromRGB(163, 162, 165), startDepth = 0, endDepth = 100, theme = nil},
	[2] = {color = Color3.fromRGB(140, 139, 141), startDepth = 101, endDepth = 250, theme = rep.Audios.LayerThemes.ShallowMine},
	[3] = {color = Color3.fromRGB(109, 107, 111), startDepth = 251, endDepth = 500, theme = rep.Audios.LayerThemes.LowerMine},
	[4] = {color = Color3.fromRGB(91, 93, 105), startDepth = 501, endDepth = 750, theme = rep.Audios.LayerThemes.DarkMine},
	[5] = {color = Color3.fromRGB(80, 80, 80), startDepth = 751, endDepth = 1000, theme = rep.Audios.LayerThemes.DeepMine},
	[6] = {color = Color3.fromRGB(0, 16, 176), startDepth = 1001, endDepth = 2000, theme = rep.Audios.LayerThemes.Mythical},
	[7] = {color = Color3.fromRGB(117, 0, 0), startDepth = 2001, endDepth = 999999999, theme = rep.Audios.LayerThemes.Hell},
}

function module.getLayerInfo(int)
	local info = nil
	for i,v in pairs(Layers) do
		if i == int then
			info = v
		end
	end
	return info
end

function module.findLayer(int)
	if not int then return nil end
	int = math.abs(int)
	local layer,info
	for i,v in pairs(Layers) do
		if v.startDepth <= int and v.endDepth >= int then
			layer,info = i,v
		end
	end
	return layer,info
end

return module

Please don’t kill me for my garbage and messy scripting. I’m already rushing my project to get it finished in-time. Don’t have time to tidy things up.

Here’s the problem I’m facing in a video: https://streamable.com/p57ct

I’m 100% sure that there is nothing wrong with the pickaxe. I’ve debugged it and checked everything and confirmed that the pickaxe has nothing to do with this issue.

If you can solve my problem, a million thanks.

Sincerely,
TheRobloxPlayer2509

1 Like

I can only take a guess because the code is rough at some points.

local function generateBlockAtPos(orePos, oldOre, moveVector)
	-- ...
	newOrePosValue.Value = orePos + moveVector -- What I think is the problem
	-- ...
end

The parameters are hard to follow, but their purpose can be determined from a function call.

generateBlockAtPos(newPos, ore, offset) -- What a call comes down to

So, you take some original orePos, add the offset to it for a newPos, and supply that to the function call. You then add the offset to it again in the line I pointed out.

You make a point to not call out your messy scripting, but if you actually want help you’d put some effort into cleaning it up, you might even find out the bug on your own and not have to ask for help.

The following code should be equivalent to what you have:

local function generateAdjacentBlock(ore, offset)

	local orePos = ore:FindFirstChild("OrePos").Value -- These FindFirstChilds make no sense here
	local newPos = orePos + offset
	local availability = checkOrePosAvailability(newPos)
	print(availability, newPos)

	if availability == true then
		generateBlockAtPos(newPos, ore, offset)
	elseif availability == "bedrock" then
		generateBedrockAtPos(newPos, ore, offset)
	end

end

--Events
mineOreEvent.OnServerEvent:Connect(function(plr, ore) -- a player mined an ore

	local oreType = ore:FindFirstChild("OreType").Value -- Not used here

	-- generate new blocks
	generateAdjacentBlock(ore, Vector3.new(1, 0, 0)) -- generate +X block
	generateAdjacentBlock(ore, Vector3.new(-1, 0, 0)) -- generate -X block
	generateAdjacentBlock(ore, Vector3.new(0, 1, 0)) -- generate +Y block
	generateAdjacentBlock(ore, Vector3.new(0, -1, 0)) -- generate -Y block
	generateAdjacentBlock(ore, Vector3.new(0, 0, 1)) -- generate +Z block
	generateAdjacentBlock(ore, Vector3.new(0, 0, -1)) -- generate -Z block

	ore:Destroy()

end)
1 Like

It’s still doing the same thing.
Here’s the output:
true – From main script, checkOrePosAvailability
true -13, -2, 2 – From main script, generateAdjacentBlock
Layer = 1 – From OreModule, module.getRandomOreForLayer
random selected ore = Stone – From OreModule, module.getRandomOreForLayer
true
true -15, -2, 2
Layer = 1
random selected ore = Stone
true
true -14, -1, 2
Layer = 1
random selected ore = Stone
true
true -14, -3, 2
Layer = 1
random selected ore = Stone
true
true -14, -2, 3
Layer = 1
random selected ore = Stone
true
true -14, -2, 1
Layer = 1
random selected ore = Stone

The issue is that you’re always trying to generate a block below, above, left, and right of where you mined. The only times this doesn’t happen is if that block is out of the boundaries you’ve given in your code. However, there’s nothing to prevent it from filling in a space that’s already been mined.

You’ll probably need to keep a record of what has been mined already in order to prevent parts from spawning in those locations.

Also based on what you have in checkOrePosAvailability

local generatedBlocks = {Vector3.new(0, 0, 0), Vector3.new(0, 1, 0)}

local function checkOrePosAvailability(orePos) -- see if a position is already generated or not
    local bool
    for i,v in pairs(generatedBlocks) do
	    if orePos == v then
		    bool = false
		    break
		else bool = true
	    end
	end
	print(bool)
	if (orePos.X > 24 or orePos.X < 0 or orePos.Z > 24 or orePos.Z < 24) and orePos.Y == -25 then
		bool = "bedrock"
	end 
	return bool
end

It looks like you were attempting to achieve this. However, generatedBlocks is what you’re comparing to in order to determine whether an area is occupied or not. This table seems to be tracking newly generated blocks I’m assuming. This isn’t particularly useful though in an attempt to prevent parts from being spawned where you’ve already mined. I would do something more along the lines of:

local generatedBlocks = {Vector3.new(0, 0, 0), Vector3.new(0, 1, 0)}
local minedBlocks = {};

local function checkOrePosAvailability(orePos) -- see if a position is already generated or not
    if minedBlocks[orePos] then
        return false
    end

    local bool
    for i,v in pairs(generatedBlocks) do
	    if orePos == v then
		    bool = false
		    break
		else bool = true
	    end
	end
	print(bool)
	if (orePos.X > 24 or orePos.X < 0 or orePos.Z > 24 or orePos.Z < 24) and orePos.Y == -25 then
		bool = "bedrock"
	end 
	return bool
end

And each time a block is mined add its coordinates to the list of minedBlocks. Don’t use table.insert for this though. Do something like:

minedBlocks[orePos] = true

in your mineOreEvent listener. In this case it’s better to use a hash rather than an array style table. The same goes for your generatedBlocks. If you used the method I just described you could do a lookup on O(1) rather than O(n) which will drastically reduce computation on large data sets.

With this improvement in mind you could simplify this function to:

local generatedBlocks = {Vector3.new(0, 0, 0), Vector3.new(0, 1, 0)}
local minedBlocks = {};

local function checkOrePosAvailability(orePos) -- see if a position is already generated or not
    if minedBlocks[orePos] or generatedBlocks[orePos] then
        return false
    end
    if (orePos.X > 24 or orePos.X < 0 or orePos.Z > 24 or orePos.Z < 24) and orePos.Y == -25 then
	return false
    end 
   return true
end

Although, I think you’ll find that this and your implementation are both wrong. I would just not use the generatedParts table to check availability.

1 Like

Thanks for all the helps you guys offered. However, the problem is still not solved. I’ll re-write my entire script, it’s the only solution I can find. Further replies will not be read.

I sorta have coded my own infinite mining system that do not have this issue, however it lack of block variation due to fact it’s a mining test game and don’t know where to start.

I made table, one for original position of the block and one for it’s 6 sides.

Everytime block was added it fetches the positions to ensure overlapping prevention.