Introduction
Hello! Recently, I started a Voxel Sandbox game! I want to make Bandwidth Optimizations to my code. The chunks that are generated on the server are sent to the client. I’ve made many optimizations to my code like making each part of a coordinate into having its own table.
Code
for x = 1,16 do
grid[x] = {}
for z = 1,16 do
local height = 32 + math.floor((math.noise(x/16, z/16) * 4) + 0.5)
grid[x][z] = {}
for y = 1,height do
local cave = math.noise(x/16, y/16, z/16)
if cave >= -0.2 then
if y == height then
grid[x][z][y] = 1
elseif y >= height - 3 and y <= height - 1 then
grid[x][z][y] = 2
elseif y >= 2 and y <= height - 4 then
grid[x][z][y] = 3
end
end
if y == 1 then
grid[x][z][y] = 4
end
end
end
end
Yet this is practical for my game with chunks with the size of about 16 32 16, which is about 16.25KB when encoded in JSON, anything higher will cause freeze-ups due to the 50KB bandwidth limitation Roblox currently has. Currently, I’ve made a way to compress data that makes it go from 16.25KB usage to just under 2 KB without caves.
How does it work?
Every loop, it checks if the block ID is different than the previous block, if so, it will save the lowest Y coordinate of the block ID’s, overall compressing data down. If a Y cord is over one block above the previous, it will put a block ID of 0 (Air block) in the lowest spot possible, allowing caves, trees, and other things. It will also add an Air block to the highest point of the slice in the chunk, fixing a bug that would cause a table overflow error.
Code
local compressedgrid = {}
for x, grid2 in pairs(grid) do
compressedgrid[x] = {}
for z, grid2 in pairs(grid2) do
compressedgrid[x][z] = {}
local LowestSimilarBlockColor = nil
for y, color in pairs(grid2) do
-- Finds if the next y cord is not a similar color
-- If it is not a similar color, it will change to the next color
if LowestSimilarBlockColor ~= color then
LowestSimilarBlockColor = color
compressedgrid[x][z][y] = color
end
-- Finds air blocks and adds a air block to the code to make decompression not have an table overflow error
if grid[x][z][y + 1] == nil then
compressedgrid[x][z][y + 1] = 0
end
end
end
end
This, however, makes the data unusable to render. So after the data is sent to the client, the client decompresses the data by looping up until it finds another block. If it finds another block with a block ID of 0, it will not add it to the decompressed data table.
Code
local decompressedgrid = {}
for x, grid in pairs(compressedgrid) do
decompressedgrid[x] = {}
for z, grid in pairs(grid) do
decompressedgrid[x][z] = {}
for y, color in pairs(grid) do
-- Checks if the y cord block put is not an air block
if color > 0 then
local Currenty = y
local Endit = false
decompressedgrid[x][z][Currenty] = color
repeat
Currenty += 1
-- Finds if next given block is not nil to end loop
if compressedgrid[x][z][Currenty] ~= nil then
Endit = true
end
-- Adds a block to the data
if not Endit then
decompressedgrid[x][z][Currenty] = color
end
until Endit
end
end
end
end
Bugs
While this sounds great, there are a few bugs. Sometimes, there will be a void in the uncompressed Voxel data, and others, not add the block.
Complete source of the code
local grid = {}
-- This creates the chunk data table
for x = 1,16 do
grid[x] = {}
for z = 1,16 do
local height = 32 + math.floor((math.noise(x/16, z/16) * 4) + 0.5)
grid[x][z] = {}
for y = 1,height do
local cave = math.noise(x/16, y/16, z/16)
if cave >= -0.2 then
if y == height then
grid[x][z][y] = 1
elseif y >= height - 3 and y <= height - 1 then
grid[x][z][y] = 2
elseif y >= 2 and y <= height - 4 then
grid[x][z][y] = 3
end
end
if y == 1 then
grid[x][z][y] = 4
end
end
end
end
local compressedgrid = {}
for x, grid2 in pairs(grid) do
compressedgrid[x] = {}
for z, grid2 in pairs(grid2) do
compressedgrid[x][z] = {}
local LowestSimilarBlockColor = nil
for y, color in pairs(grid2) do
-- Finds if the next y cord is not a similar color
-- If it is not a similar color, it will change to the next color
if LowestSimilarBlockColor ~= color then
LowestSimilarBlockColor = color
compressedgrid[x][z][y] = color
end
-- Finds air blocks and adds a air block to the code to make decompression not have table overflow error
if grid[x][z][y + 1] == nil then
compressedgrid[x][z][y + 1] = 0
end
end
end
end
local decompressedgrid = {}
for x, grid in pairs(compressedgrid) do
decompressedgrid[x] = {}
for z, grid in pairs(grid) do
decompressedgrid[x][z] = {}
for y, color in pairs(grid) do
-- Checks if the y cord block put is not an air block
if color > 0 then
local Currenty = y
local Endit = false
decompressedgrid[x][z][Currenty] = color
repeat
Currenty += 1
-- Finds if next given block is not nil to end loop
if compressedgrid[x][z][Currenty] ~= nil then
Endit = true
end
-- Adds a block to the data
if not Endit then
decompressedgrid[x][z][Currenty] = color
end
until Endit
end
end
end
end
-- This generates a chunk with no sort of compression algorithm
for x, grid in pairs(grid) do
for z, grid in pairs(grid) do
for y, color in pairs(grid) do
local p = Instance.new("Part", workspace)
p.Size = Vector3.new(3,3,3)
p.Position = Vector3.new(x * 3, y * 3, z * 3)
p.Anchored = true
if color == 1 then
p.Color = Color3.fromRGB(0,200,0)
elseif color == 2 then
p.Color = Color3.fromRGB(120, 60, 0)
elseif color == 3 then
p.Color = Color3.fromRGB(150,150,150)
elseif color == 4 then
p.Color = Color3.fromRGB(100,100,100)
end
end
end
end
-- This uses the Decompressed data table to generate the chunk
for x, grid in pairs(decompressedgrid) do
for z, grid in pairs(grid) do
for y, color in pairs(grid) do
local p = Instance.new("Part", workspace)
p.Size = Vector3.new(3,3,3)
p.Position = Vector3.new((x - 32) * 3, y * 3, z * 3)
p.Anchored = true
if color == 1 then
p.Color = Color3.fromRGB(0,200,0)
elseif color == 2 then
p.Color = Color3.fromRGB(120, 60, 0)
elseif color == 3 then
p.Color = Color3.fromRGB(150,150,150)
elseif color == 4 then
p.Color = Color3.fromRGB(100,100,100)
end
end
end
end
Does anyone know why that’s the case, and if so, is there a fix? If not, does anyone have any other ways to compress data? Thank you for your time to read my page and for your assistance!