CC-navigation/maps/tinyMap.lua
2016-08-13 08:57:08 +01:00

245 lines
6.4 KiB
Lua

local BIT_MASKS = {
SET_COORD = bit.blshift(1, 5),
SET_X_COORD = bit.blshift(1, 4),
COORD = 15,
COORD_DATA = bit.blshift(1, 4),
}
local function base256_to_base64(input)
local output = {}
for i = 1, #input, 3 do
table.insert(output, bit.brshift(input[i] or 0, 2))
table.insert(output, bit.blshift(bit.band(input[i] or 0, 3), 4) + bit.brshift(input[i+1] or 0, 4))
table.insert(output, bit.blshift(bit.band(input[i+1] or 0, 15), 2) + bit.brshift(input[i+2] or 0, 6))
table.insert(output, bit.band(input[i+2] or 0, 63))
end
return output
end
local function base64_to_base256(input)
local output = {}
for i = 1, #input, 4 do
table.insert(output, bit.blshift(input[i] or 0, 2) + bit.brshift(input[i+1] or 0, 4))
table.insert(output, bit.blshift(bit.band(input[i+1] or 0, 15), 4) + bit.brshift(input[i+2] or 0, 2))
table.insert(output, bit.blshift(bit.band(input[i+2] or 0, 3), 6) + (input[i+3] or 0))
end
return output
end
local function isValidValue(value)
return value == nil or value == 0 or value == 1
end
local function toGridCode(tVector)
return math.floor(tVector.x/16), math.floor(tVector.y/16), math.floor(tVector.z/16), tVector.x % 16, tVector.y % 16, tVector.z % 16
end
local function setGrid(tMap, x, y, z, grid)
if not tMap.map[x] then
tMap.map[x] = {}
end
if not tMap.map[x][y] then
tMap.map[x][y] = {}
end
tMap.map[x][y][z] = grid
return tMap.map[x][y][z]
end
local function getGrid(tMap, x, y, z)
if not tMap.map[x] or not tMap.map[x][y] or not tMap.map[x][y][z] then
return tMap:load(x, y, z)
else
return tMap.map[x][y][z]
end
end
local mapMethods = {
getGrid = function(self, tVector, y, z)
local gX, gY, gZ
if y and z then
gX, gY, gZ = tVector, y, z
else
gX, gY, gZ = toGridCode(tVector)
end
return getGrid(self, gX, gY, gZ)
end,
load = function(self, tVector, y, z)
local gX, gY, gZ
if y and z then
gX, gY, gZ = tVector, y, z
else
gX, gY, gZ = toGridCode(tVector)
end
local gridPath = fs.combine(self.mapDir, gX..","..gY..","..gZ)
if fs.exists(gridPath) then
local handle = fs.open(gridPath, "rb")
if handle then
local grid = {}
local rawData = {}
local rawDataByte = handle.read()
while rawDataByte do
table.insert(rawData, rawDataByte)
rawDataByte = handle.read()
end
handle.close()
--local data = rawData
local data = base256_to_base64(rawData)
--load grid data
local currX, currY, currZ
local dataByte
for _, dataByte in ipairs(data) do
if bit.band(dataByte, BIT_MASKS.SET_COORD) == BIT_MASKS.SET_COORD then
--we are changing our currX or currY coord
if bit.band(dataByte, BIT_MASKS.SET_X_COORD) == BIT_MASKS.SET_X_COORD then
--we are changing our currX coord
currX = bit.band(dataByte, BIT_MASKS.COORD)
else
--we are changing our currY coord
currY = bit.band(dataByte, BIT_MASKS.COORD)
end
else
--we are setting the value for a proper coord
currZ = bit.band(dataByte, BIT_MASKS.COORD)
if currX and currY and currZ then
if not grid[currX] then
grid[currX] = {}
end
if not grid[currX][currY] then
grid[currX][currY] = {}
end
grid[currX][currY][currZ] = bit.brshift(bit.band(dataByte, BIT_MASKS.COORD_DATA), 4)
end
end
end
return setGrid(self, gX, gY, gZ, grid)
end
end
return setGrid(self, gX, gY, gZ, {})
end,
loadAll = function(self)
if fs.exists(self.mapDir) and fs.isDir(self.mapDir) then
for _, gridFile in ipairs(fs.list(self.mapDir)) do
local _, _, gX, gY, gZ = string.find(gridFile, "(.+)%,(.+)%,(.+)")
if gX and gY and gX then
self:load(tonumber(gX), tonumber(gY), tonumber(gZ))
end
end
end
end,
save = function(self, tVector, y, z)
local gX, gY, gZ
if y and z then
gX, gY, gZ = tVector, y, z
else
gX, gY, gZ = toGridCode(tVector)
end
if self.map[gX] and self.map[gX][gY] and self.map[gX][gY][gZ] then
local grid = self.map[gX][gY][gZ]
if next(grid) then
local rawData = {}
for x, gridYZ in pairs(grid) do
table.insert(rawData, BIT_MASKS.SET_COORD + BIT_MASKS.SET_X_COORD + x)
for y, gridZ in pairs(gridYZ) do
table.insert(rawData, BIT_MASKS.SET_COORD + y)
for z, coordValue in pairs(gridZ) do
table.insert(rawData, bit.blshift(coordValue, 4) + z)
end
end
end
--local data = rawData
local data = base64_to_base256(rawData)
local handle = fs.open(fs.combine(self.mapDir, gX..","..gY..","..gZ), "wb")
if handle then
for _, dataByte in ipairs(data) do
handle.write(dataByte)
end
handle.close()
end
else
fs.delete(fs.combine(self.mapDir, gX..","..gY..","..gZ))
end
end
end,
saveAll = function(self)
for gX, YZmap in pairs(self.map) do
for gY, Zmap in pairs(YZmap) do
for gZ, grid in pairs(Zmap) do
self:save(gX, gY, gZ)
end
end
end
end,
get = function(self, tVector)
local gX, gY, gZ, pX, pY, pZ = toGridCode(tVector)
local grid = getGrid(self, gX, gY, gZ)
if grid[pX] and grid[pX][pY] then
return grid[pX][pY][pZ]
end
end,
set = function(self, tVector, value)
if not isValidValue(value) then
--should we throw an error or use a default value?
error("set: value is not valid")
end
local gX, gY, gZ, pX, pY, pZ = toGridCode(tVector)
local grid = getGrid(self, gX, gY, gZ)
if not grid[pX] then
grid[pX] = {}
end
if not grid[pX][pY] then
grid[pX][pY] = {}
end
grid[pX][pY][pZ] = value
return grid[pX][pY][pZ]
end,
getOrSet = function(self, tVector, value)
local gX, gY, gZ, pX, pY, pZ = toGridCode(tVector)
local grid = getGrid(self, gX, gY, gZ)
if grid[pX] and grid[pX][pY] and grid[pX][pY][pZ] then
return grid[pX][pY][pZ], false
else
if not isValidValue(value) then
--should we throw an error or use a default value?
error("getOrSet: value is not valid")
end
if not grid[pX] then
grid[pX] = {}
end
if not grid[pX][pY] then
grid[pX][pY] = {}
end
grid[pX][pY][pZ] = value
return grid[pX][pY][pZ], true
end
end,
}
local mapMetatable = {__index = mapMethods}
function new(mapDir)
local tMap = {}
if type(mapDir) == "string" then
if not fs.exists(mapDir) then
fs.makeDir(mapDir)
elseif not fs.isDir(mapDir) then
error("new: not a valid directory")
end
tMap.mapDir = mapDir
else
error("new: directory must be string")
end
tMap.map = {}
setmetatable(tMap, mapMetatable)
return tMap
end