Why is this happening?

I have a 3D perlin noise generator, and I decided to work on a script that makes it generate terrain around the player. While I was in the middle of scripting it I noticed a bug which I can’t fix. Can someone help me?

Code:

local chunks = {}

local function chunkExists(chunkX, chunkZ, chunkY) -- Check's if a chunk exists
	if not chunks[chunkX] then
		chunks[chunkX] = {}
		
	end
	
	return chunks[chunkX][chunkZ][chunkY]
end

local function roundTo(number, roundToNumber) -- Give it a value and it'll round it to the target's multiple, e.g 12 goes to 8, 17 goes to 16.
	return number - (number % roundToNumber)
end

local function CheckPos(X, Z, Y)
	local RoundedX = roundTo(X, 32)
	local RoundedZ = roundTo(Z, 32)
	local RoundedY = roundTo(Y, 32)
	
	print(" X: "..RoundedX.." Z: "..RoundedZ.." Y: "..RoundedY)
	--print(RoundedZ)
	--print(RoundedY)
	
	for x = -32, 32 do
		for z = -32, 32 do
			for y = -32, 32 do
				local chunkX = x + RoundedX
				local chunkZ = z + RoundedZ
				local chunkY = y + RoundedY
				
				if not chunkExists(chunkX, chunkZ, chunkY) then
					chunks[chunkX][chunkZ][chunkY] = true
				end
			end
		end
	end
end

spawn(function()
	while true do
		wait(.1)
		--print("hh")
		for i, v in pairs(game:GetService("Players"):GetChildren()) do
			local C = nil
			local HRP = nil
			
			pcall(function()
				if v.Character then
					C = v.Character
				else
					C = nil
				end
			end)
			
			if C ~= nil then
				pcall(function()
					if C:FindFirstChild("HumanoidRootPart") then
						HRP = C:FindFirstChild("HumanoidRootPart")
						
					else
						HRP = nil
					end
				end)
			end
			
			if HRP ~= nil then
				CheckPos(HRP.Position.X, HRP.Position.Z, HRP.Position.Y)
			end
		end
	end
end)

The script isn’t finished because I found this bug before it was even close to finished.

I get an error message of: Attempt to Index a nil value in the ChunkExists Function

Inside your chunkExists function, there is a bug. If chunks[chunkX] doesn’t exist, then you initialize it with an empty table. However, in the next part of the function you attempt to get chunks[chunkX][chunkZ][chunkY], but remember chunks[chunkX] might just be an empty table. Hence chunks[chunkX][chunkZ] will be nil, so you cannot try to get nil[chunkY]. This throws the error.

5 Likes

It only throws an error when it returns x, z, y return chunks[chunkX][chunkZ][chunkY]

But for some reason I don’t get this error when trying to return just x, z. Like so:

return chunks[chunkX][chunkZ]

I actually get a different error apparently when only x, z are returned. :eyes:

The above reply already explains the reason for this behaviour. A single-dimension lookup won’t error (or shouldn’t) because you’re looking up the value assigned to the chunkZ parameter which is nil in the constructed table. That’s assuming your code doesn’t assume non-nil returns from chunkExists.

2 Likes

So can’t we just use an if else statement to prevent this from happening? Like so:

if chunks[chunkX][chunkZ][chunkY] == nil then
    return "It doesnt exist" -- Or you can return nil or false
end

No, what if chunks[chunkX][chunkZ] is nil? You have to do a step by step if statement.

2 Likes

That will error because chunks[chunkX][chunkZ] is nil. You can’t perform a lookup at a nil indice.

  • You can’t perform a lookup on Z if X is nil.
  • You can’t perform a lookup on Y if Z is nil.

It still follows the same problem as the original code.

2 Likes

If you wanted to do this, you have to check step by step:

if not chunks[chunkX] then
   return false
end
if not chunks[chunkX][chunkZ] then
   return false
end
if not chunks[chunkX][chunkZ][chunkY] then
   return false
end
--chunks[chunkX][chunkZ][chunkY] is valid.

Another alternative is to store the index as a string like chunks["X: 5, Y: 10, Z:20"]. But it’s up to you.

3 Likes

Given your system and the arbitrary values it passes to perform lookups under chunks, yes you have to check each dimension.

2 Likes

So could we fix this issue like this:

local chunks = {}

local function chunkExists(chunkX, chunkZ, chunkY) -- Check's if a chunk exists
	local isNil = false

        if not chunks[chunkX] then
		chunks[chunkX] = {}
		
	end
	if not chunks[chunkX] then
            isNil = true
            return false
        end
        if not chunks[chunkX][chunkZ] then
            isNil = true
            return false
        end
        if not chunks[chunkX][chunkZ][chunkY] then
            isNil = true
            return false
        end
--chunks[chunkX][chunkZ][chunkY] is valid.
        if isNil == false then
        	return chunks[chunkX][chunkZ][chunkY]
        end
end

?

If you’re interested in a slightly expensive way to do this, you can wrap the chunks table with a metatable which initialises new dimensions. I don’t really recommend it though. It makes many assumptions about the code (such as that values will always be tables), among other things.

local mt = {
    __index = function(self, index)
        if not self[index] then
            self[index] = setmetatable({}, mt)
            return self[index]
        end
    end
}

local chunks = setmetatable({}, mt)

Worth the attempt. Wrote it from mobile so I can’t test it.

Was the code I mentioned here correct?

Ahh… I see. Your on mobile atm. Respond whenever you have time.

I have added this to the current code but it gets a lag spike every 5 seconds. What can I do to stop this from lagging:

local chunks = {}

local function chunkExists(chunkX, chunkZ, chunkY) -- Check's if a chunk exists
   local isNil = false

   if not chunks[chunkX] then
      chunks[chunkX] = {}
		
   end
   if not chunks[chunkX] then
       isNil = true
       return false
   end
   if not chunks[chunkX][chunkZ] then
       isNil = true
       return false
   end
   if not chunks[chunkX][chunkZ][chunkY] then
       isNil = true
       return false
   end

   if isNil == false then
        return chunks[chunkX][chunkZ][chunkY]
   end
end

local function roundTo(number, roundToNumber) -- Give it a value and it'll round it to the target's multiple, e.g 12 goes to 8, 17 goes to 16.
	return number - (number % roundToNumber)
end

local function CheckPos(X, Z, Y)
	local RoundedX = roundTo(X, 32)
	local RoundedZ = roundTo(Z, 32)
	local RoundedY = roundTo(Y, 32)
	
	--print(" X: "..RoundedX.." Z: "..RoundedZ.." Y: "..RoundedY)
	--print(RoundedZ)
	--print(RoundedY)
	
	for x = -32, 32 do
		for z = -32, 32 do
			for y = -32, 32 do
				local cx = RoundedX + x
				local cz = RoundedZ + z
				local cy = RoundedY + y
				
				if not chunkExists(cx, cz, cy) then
					
				end
			end
		end
	end
end

spawn(function()
	while true do
		wait(5)
		--print("hh")
		for i, v in pairs(game:GetService("Players"):GetChildren()) do
			local C = nil
			local HRP = nil
			
			pcall(function()
				if v.Character then
					C = v.Character
				else
					C = nil
				end
			end)
			
			if C ~= nil then
				pcall(function()
					if C:FindFirstChild("HumanoidRootPart") then
						HRP = C:FindFirstChild("HumanoidRootPart")
						
					else
						HRP = nil
					end
				end)
			end
			
			if HRP ~= nil then
				CheckPos(HRP.Position.X, HRP.Position.Z, HRP.Position.Y)
			end
		end
	end
end)

I seemed to fix this issue by wrapping the CheckPos function with spawn(function() like so:

local function CheckPos(X, Z, Y)
	spawn(function()
		local RoundedX = roundTo(X, 32)
		local RoundedZ = roundTo(Z, 32)
		local RoundedY = roundTo(Y, 32)
		
		--print(" X: "..RoundedX.." Z: "..RoundedZ.." Y: "..RoundedY)
		--print(RoundedZ)
		--print(RoundedY)
		
		for x = -32, 32 do
			for z = -32, 32 do
				for y = -32, 32 do
					local cx = RoundedX + x
					local cz = RoundedZ + z
					local cy = RoundedY + y
					
					if not chunkExists(cx, cz, cy) then
						
					end
				end
			end
		end
	end)
end

Seems like you fixed my issue. Thank you SOOOOO much @colbert2677, and @REALTimothy0812 for helping me out.

I honestly think I should learn more about tables.

1 Like

One issue that I discovered just now is at line 53. I get a similar error. But at a different line. I think its related to the issue that I specified in this topic. For the sake of helping me get this solved (Because I don’t know how to fix it atm) I will post the code here for anyone who wants to help me fix it:

local chunks = {}

local function chunkExists(chunkX, chunkZ, chunkY) -- Check's if a chunk exists
        spawn(function()
    local isNil = false

  -- if not chunks[chunkX] then
  --    chunks[chunkX] = {}

  -- end
   if not chunks[chunkX] then
       chunks[chunkX] = {}
     --  return false
   end
   if not chunks[chunkX][chunkZ] then
       chunks[chunkX][chunkZ] = {}
     --  return false
   end
   if not chunks[chunkX][chunkZ][chunkY] then
       chunks[chunkX][chunkZ][chunkY] = {}
       --return false
   end

  -- if isNil == false then
       return chunks[chunkX][chunkZ][chunkY]
  -- end
    end)
end

local function roundTo(number, roundToNumber) -- Give it a value and it'll round it to the target's multiple, e.g 12 goes to 8, 17 goes to 16.
    return number - (number % roundToNumber)
end

local function CheckPos(X, Z, Y)
    spawn(function() -- We use spawn function because if we don't then every time this function is called then the (all) the client(s) that are running the game will freeze
        local RoundedX = roundTo(X, 32)
        local RoundedZ = roundTo(Z, 32)
        local RoundedY = roundTo(Y, 32)

        --print(" X: "..RoundedX.." Z: "..RoundedZ.." Y: "..RoundedY)
        --print(RoundedZ)
        --print(RoundedY)

        for x = -32, 32 do
            for z = -32, 32 do
                for y = -32, 32 do
                    local cx = RoundedX + x
                    local cz = RoundedZ + z
                    local cy = RoundedY + y

                    if not chunkExists(cx, cz, cy) == true then
                        --print("x: "..cx.." z: "..cz.." y: "..cy)
                        chunks[cx][cz][cy] = true -- This is where I am getting the error at :/

                    end
                end
            end
        end
    end)
end

spawn(function()
    while true do
        wait(1)
        --print("hh")
        for i, v in pairs(game:GetService("Players"):GetChildren()) do
            local C = nil
            local HRP = nil

            pcall(function()
                if v.Character then
                    C = v.Character
                else
                    C = nil
                end
            end)

            if C ~= nil then
                pcall(function()
                    if C:FindFirstChild("HumanoidRootPart") then
                        HRP = C:FindFirstChild("HumanoidRootPart")

                    else
                        HRP = nil
                    end
                end)
            end

            if HRP ~= nil then
                CheckPos(HRP.Position.X, HRP.Position.Z, HRP.Position.Y)
            end
        end
    end
end)