Voxels sometimes generate off grid by like 0.00003 studs

I’ve been looking into an issue in my voxel generation system. The issue is that sometimes some voxels would spawn inside other voxels. This thing shouldn’t be possible at all since I save the used voxel positions in a table.
The reason this happens is because for whatever reason the location is off by 0.0003 studs or something like this.

[2158] = 509.9552, 2.09640503, 223.993958,
[2204] = 509.95517, 2.09640503, 223.993958,

image

Can someone please help me figure out why this happens?
Please, i am going clinically insane, i’ve spent hours on this and im officially blind because i can not see what can freaking cause this

function MiningService:GenerateMine(startPos, endPos, CONFIG)
    local voxels_in_X_row = math.floor((endPos.X - startPos.X) / 4)
    local voxels_in_Y_row = math.floor((endPos.Y - startPos.Y) / 4)

    local mine = CONFIG.Name
    local orePlan = LootPlan.new("Single")
    orePlan:AddLootFromTable(CONFIG.Chances)

    --Generated the first row of voxels on X and Y axis
    for i = 1, voxels_in_X_row do
        for j = 1, voxels_in_Y_row do
            local voxelPos = Vector3.new(startPos.X + i * 4, startPos.Y + j * 4, startPos.Z)
            local ore = orePlan:GetRandomLoot()
            self:CreateVoxel(voxelPos, ore, mine)
        end
    end
end
function MiningService:CreateVoxel(position, oreName, mine)
    if table.find(self.PreviouslyGeneratedVoxels, position) then return warn("!") end

    self.OresSpawned += 1
    local id = self.OresSpawned

    local voxelData = {
        Id = id,
        Name = oreName,
        Mine = mine,
        HP = OreData[oreName].HP,
        MaxHP = OreData[oreName].HP,
    }

    id = tostring(id)
    table.insert(self.PreviouslyGeneratedVoxels, position)
    VoxelReplica.VoxelData:SetValue({'X', id}, position.X)
    VoxelReplica.VoxelData:SetValue({'Y', id}, position.Y)
    VoxelReplica.VoxelData:SetValue({'Z', id}, position.Z)
    VoxelReplica.VoxelData:SetValue({'VoxelIDs', id}, voxelData)
end
function MiningService:GenerateVoxelsAroundVoxel(voxelData, voxelPos)
    local startPos = workspace.Mines.Mine1.Start.Position
    local endPos = workspace.Mines.Mine1.End.Position
    local pos1 = voxelPos
    for _, pos2 in pairs(positions) do
        local newPos = pos1+pos2
        local wasGenerated


        if table.find(self.PreviouslyGeneratedVoxels, newPos) then
            wasGenerated = true
        end
    
        if newPos.Z <= startPos.Z or newPos.Y <= startPos.Y then
            wasGenerated = true
        end

        if not wasGenerated then
            local mine = voxelData.Mine
            local CONFIG = require(Shared.CONFIG.Mines[mine])

            local randomOre = LootPlan.new("Single")
            randomOre:AddLootFromTable(CONFIG.Chances)
            randomOre = randomOre:GetRandomLoot()
            self:CreateVoxel(newPos, randomOre, mine)
        end
    end
end
function MiningService:BreakVoxel(player, voxelData)
    if not voxelData then return end
    if table.find(usedIds, voxelData.Id) then return warn("Duplicated found") end
    local oreName = voxelData.Name
    local id = tostring(voxelData.Id)
    local X = VoxelReplica.VoxelData.Data.X[id]
    local Y = VoxelReplica.VoxelData.Data.Y[id]
    local Z = VoxelReplica.VoxelData.Data.Z[id]
    local voxelPos = Vector3.new(X, Y, Z)

    
    
    table.insert(usedIds, id)
    createDropSignal:Fire(player, oreName, voxelPos, id)


    VoxelReplica.VoxelData:SetValue({'VoxelIDs', id}, nil)
    VoxelReplica.VoxelData:SetValue({'X', id}, nil)
    VoxelReplica.VoxelData:SetValue({'Y', id}, nil)
    VoxelReplica.VoxelData:SetValue({'Z', id}, nil)

    self:GenerateVoxelsAroundVoxel(voxelData, voxelPos)
end

You should probably use math.round() on the voxel’s position to make sure that they don’t generate inside each other.

That is one option but i’d rather use the exact position without rounding (will probably do this if i can not figure out why this happens)
Im more interested in why there is this extremely small difference in positions
What would cause this to be off by such a small number

How do you create the voxels? Are they “Instance.new”'d in or are they cloned in?

I posted the scripts here
The voxel data (which includes voxel position) is created on the server while the client handles the visuals

The data i posted here is straight from the server which includes how the position is calculated

1 Like

Sorry then, I’m not reading through all that code lol. It’s wayyyy too late.

Yeah seems to work fine if i round the position
Still weird how roblox can’t handle small numbers since there is absolutely no reason for that small difference in positions to happen :confused:

Well whatever no reason to scramble my brains over this… if it works it works /shrug

If you “Instance.new” the parts in my guess is they are affected by gravity before being anchored. But that is just a guess and there is a high chance I’m completely wrong lol.

That’s impossible because the server is the one calculating the position of the voxels
There is nothing visual happening on the server

The client only clones and places voxels based on what data it receives from the server

Anyway i also got an answer on HiddenDevs on discord
Idk if this info is true or not but if it is… this sucks… lol

Anyway in the end im probably gonna stick with math.round() because its also a good way to save data and internet usage for the players since the server will only be sending numbers like Vector3.new(524,543,436) and not Vector3.new(524.534654645, 543.5436546, 436.6547657)

Oh ok I see.
303030303030303030