Add files

This commit is contained in:
Matt Blunt
2016-02-07 18:20:06 +00:00
parent d63007f101
commit c0673612b1
14 changed files with 2204 additions and 0 deletions

209
maps/compactMap Normal file
View File

@@ -0,0 +1,209 @@
local BIT_MASKS = {
SET_COORD = bit.blshift(1, 7),
SET_X_COORD = bit.blshift(1, 6),
COORD = 15,
COORD_DATA = bit.blshift(7, 4),
}
local function isValidValue(value)
return value == nil or (type(value) == "number" and value % 1 == 0 and value >= 0 and value <= 7)
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 = {}
--load grid data
local currX, currY, currZ
local dataByte = handle.read()
while dataByte 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
dataByte = handle.read()
end
handle.close()
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 handle = fs.open(fs.combine(self.mapDir, gX..","..gY..","..gZ), "wb")
if handle then
for x, gridYZ in pairs(grid) do
handle.write(BIT_MASKS.SET_COORD + BIT_MASKS.SET_X_COORD + x)
for y, gridZ in pairs(gridYZ) do
handle.write(BIT_MASKS.SET_COORD + y)
for z, coordValue in pairs(gridZ) do
handle.write(bit.blshift(coordValue, 4) + z)
end
end
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("compactMap set: value is not valid", 2)
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("compactMap getOrSet: value is not valid", 2)
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("compactMap new: not a valid directory", 2)
end
tMap.mapDir = mapDir
else
error("compactMap new: directory must be string", 2)
end
tMap.map = {}
setmetatable(tMap, mapMetatable)
return tMap
end

142
maps/map Normal file
View File

@@ -0,0 +1,142 @@
local maps = {}
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 methods = {
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
if self.name then
if fs.exists(".maps/"..self.name.."/"..gX..","..gY..","..gZ) then
local handle = fs.open(".maps/"..self.name.."/"..gX..","..gY..","..gZ, "r")
if handle then
local grid = handle.readAll()
handle.close()
for i = 15, 0, -1 do
grid = string.gsub(grid, tostring(i).."=", "%["..tostring(i).."%]=")
end
grid = textutils.unserialize(grid)
if type(grid) == "table" then
return setGrid(self, gX, gY, gZ, grid)
end
end
end
end
return setGrid(self, gX, gY, gZ, {})
end,
loadAll = function(self)
if self.name and fs.exists(".maps/"..self.name) and fs.isDir(".maps/"..self.name) then
for _, gridFile in ipairs(fs.list(".maps/"..self.name)) 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)
if self.name then
local saveDir = ".maps/"..self.name
for x, YZmap in pairs(self.map) do
for y, Zmap in pairs(YZmap) do
for z, grid in pairs(Zmap) do
if next(grid) then
local handle = fs.open(fs.combine(saveDir, x..","..y..","..z), "w")
local data = textutils.serialize(grid)
data = string.gsub(data, " ", "")
for i = 0, 15 do
data, num = string.gsub(data, "%["..tostring(i).."%]=", tostring(i).."=")
end
handle.write(data)
handle.close()
end
end
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)
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 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,
}
function new(name)
local tMap = {}
if name and type(name) == "string" then
if maps[name] then
return maps[name]
end
tMap.name = name
if not fs.exists(".maps/"..name) then
fs.makeDir(".maps/"..name)
end
maps[name] = tMap
end
tMap.map = {}
setmetatable(tMap, {__index = methods})
return tMap
end

185
maps/remoteMap_client Normal file
View File

@@ -0,0 +1,185 @@
local REDNET_TIMEOUT = 1
local MESSAGE_TYPE = {
GET = 0,
SET = 1,
}
local function newMessage(messageType, grid, data)
return {
type = messageType,
ID = math.random(0, 2^30),
grid = grid,
data = data,
}
end
local function sendAndWaitForResponse(recipientID, message, protocol)
rednet.send(recipientID, message, protocol)
local attemptNumber = 1
while true do
local senderID, reply = rednet.receive(protocol, REDNET_TIMEOUT)
if senderID == recipientID and type(reply) == "table" and reply.type == message.type and reply.ID == message.ID then
return reply.data
elseif not senderID then
if attemptNumber < 3 then
rednet.send(recipientID, message, protocol)
attemptNumber = attemptNumber + 1
else
return false
end
end
end
end
local function isValidValue(value)
return value == nil or value == -1 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 getRemoteGrid(tMap, x, y, z)
if not tMap.remoteGrids[x] or not tMap.remoteGrids[x][y] or not tMap.remoteGrids[x][y][z] then
local message = newMessage(MESSAGE_TYPE.GET, {x, y, z}, nil)
local remoteGrid = sendAndWaitForResponse(tMap.serverID, message, tMap.protocol) or {}
tMap.remoteGridsAge[x..","..y..","..z] = os.clock()
if not tMap.remoteGrids[x] then
tMap.remoteGrids[x] = {}
end
if not tMap.remoteGrids[x][y] then
tMap.remoteGrids[x][y] = {}
end
tMap.remoteGrids[x][y][z] = remoteGrid
return remoteGrid
else
return tMap.remoteGrids[x][y][z]
end
end
local function getUpdateGrid(tMap, x, y, z)
if not tMap.updateGrids[x] or not tMap.updateGrids[x][y] or not tMap.updateGrids[x][y][z] then
local updateGrid = {}
if not tMap.updateGrids[x] then
tMap.updateGrids[x] = {}
end
if not tMap.updateGrids[x][y] then
tMap.updateGrids[x][y] = {}
end
tMap.updateGrids[x][y][z] = updateGrid
return updateGrid
else
return tMap.updateGrids[x][y][z]
end
end
local remoteMapMethods = {
get = function(self, coord)
local gX, gY, gZ, pX, pY, pZ = toGridCode(coord)
local grid = getRemoteGrid(self, gX, gY, gZ)
if grid[pX] and grid[pX][pY] then
return grid[pX][pY][pZ]
end
end,
set = function(self, coord, value)
if not isValidValue(value) then
--should we throw an error or use a default value?
error("remoteMap set: value is not valid", 2)
end
local gX, gY, gZ, pX, pY, pZ = toGridCode(coord)
local grid = getUpdateGrid(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
end,
check = function(self)
local time = os.clock()
local newRemoteGridsAge = {}
for gridCode, gridAge in pairs(self.remoteGridsAge) do
if time - gridAge >= self.timeout then
local x, y, z = string.match(gridCode, "([-]?%d+),([-]?%d+),([-]?%d+)")
x, y, z = tonumber(x), tonumber(y), tonumber(z)
if x and y and z then
if self.remoteGrids[x] and self.remoteGrids[x][y] then
self.remoteGrids[x][y][z] = nil
end
end
else
newRemoteGridsAge[gridCode] = gridAge
end
end
local newUpdateGridsAge = {}
for gridCode, gridAge in pairs(self.updateGridsAge) do
if time - gridAge >= self.timeout then
-- remove grid from updateGrids ???
else
newUpdateGridsAge[gridCode] = gridAge
end
end
self.remoteGridsAge = newRemoteGridsAge
self.updateGridsAge = newUpdateGridsAge
end,
pushUpdates = function(self, ignoreTimeout)
local newUpdateGrids = {}
for gX, YZmap in pairs(self.updateGrids) do
newUpdateGrids[gX] = {}
for gY, Zmap in pairs(YZmap) do
newUpdateGrids[gX][gY] = {}
for gZ, grid in pairs(Zmap) do
local gridCode = gX..","..gY..","..gZ
if next(grid) then
if ignoreTimeout == true or (not self.updateGridsAge[gridCode]) or os.clock() - self.updateGridsAge[gridCode] >= self.timeout then
local message = newMessage(MESSAGE_TYPE.SET, {gX, gY, gZ}, grid)
local response = sendAndWaitForResponse(self.serverID, message, self.protocol)
if response == true then
self.updateGridsAge[gridCode] = os.clock()
else
newUpdateGrids[gX][gY][gZ] = grid
end
else
newUpdateGrids[gX][gY][gZ] = grid
end
end
end
end
end
self.updateGrids = newUpdateGrids
end,
}
local remoteMapMetatable = {__index = remoteMapMethods}
function new(mapName, timeout)
if type(mapName) ~= "string" then
error("mapName must be string")
end
if type(timeout) ~= "number" or timeout < 0 then
error("timeout must be positive number")
end
local protocol = "map_share:"..mapName
local serverID = rednet.lookup(protocol, "SERVER")
if not serverID then
error("could not find map share server")
end
local remoteMap = {
mapName = mapName,
protocol = protocol,
serverID = serverID,
timeout = timeout,
remoteGrids = {},
remoteGridsAge = {},
updateGrids = {},
updateGridsAge = {},
}
return setmetatable(remoteMap, remoteMapMetatable)
end

163
maps/remoteMap_server Normal file
View File

@@ -0,0 +1,163 @@
local args = {...}
--===== OPEN REDNET =====--
for _, side in ipairs(redstone.getSides()) do
if peripheral.getType(side) == "modem" then
rednet.open(side)
end
end
if not rednet.isOpen() then
printError("could not open rednet")
return
end
--===== LOAD MAP =====--
if not compactMap then
if not os.loadAPI("compactMap") then
error("could not load API: compactMap")
end
end
local map = compactMap.new(args[1])
--===== SET REDNET PROTOCOL =====--
local MAP_SHARE_PROTOCOL
if args[2] and type(args[2]) == "string" then
MAP_SHARE_PROTOCOL = "map_share:"..args[2]
else
MAP_SHARE_PROTOCOL = "map_share:"..fs.getName(args[1])
end
--===== HOST AS SERVER =====--
do
local host = rednet.lookup(MAP_SHARE_PROTOCOL, "SERVER")
if host and host ~= os.computerID() then
printError("server already running for this map share")
return
end
end
rednet.host(MAP_SHARE_PROTOCOL, "SERVER")
--===== UTILS =====--
local MESSAGE_TYPE = {
GET = 0,
SET = 1,
}
local receivedMessages = {}
local receivedMessageTimeouts = {}
local function isValidCoord(coord)
return type(coord) == "number" and coord % 1 == 0 and coord >= 0 and coord <= 15
end
local function updateCoord(x, y, z, value)
local coord = vector.new(x, y, z)
local currValue = map:get(coord)
if value == 1 then
if currValue then
map:set(coord, math.min(7, currValue + 1))
else
map:set(coord, 0)
end
elseif value == -1 then
if currValue then
if currValue == 0 then
map:set(coord, nil)
else
map:set(coord, currValue - 1)
end
end
end
end
local function updateMap(newData, gX, gY, gZ)
if type(newData) == "table" then
local currX, currY
for x, gridYZ in pairs(newData) do
if isValidCoord(x) and type(gridYZ) == "table" then
currX = gX*16 + x
for y, gridZ in pairs(gridYZ) do
if isValidCoord(y) and type(gridZ) == "table" then
currY = gY*16 + y
for z, value in pairs(gridZ) do
if isValidCoord(z) then
updateCoord(currX, currY, gZ*16 + z, value)
end
end
end
end
end
end
map:save(gX, gY, gZ)
end
end
local function checkGridCoordFormat(gridCoord)
if type(gridCoord) == "table" and #gridCoord == 3 then
for i = 1, 3 do
local coord = gridCoord[i]
if type(coord) ~= "number" or coord % 1 ~= 0 then
return false
end
end
return true
end
return false
end
local function newMessage(messageType, messageID, grid, data)
return {
type = messageType,
ID = messageID,
grid = grid,
data = data,
}
end
--===== REPEATED MESSAGE HANDLING =====--
local function clearOldMessages()
while true do
local event, timer = os.pullEvent("timer")
local messageID = receivedMessageTimeouts[timer]
if messageID then
receivedMessageTimeouts[timer] = nil
receivedMessages[messageID] = nil
end
end
end
--===== MAIN =====--
local function main()
while true do
local senderID, message = rednet.receive(MAP_SHARE_PROTOCOL)
if type(message) == "table" and checkGridCoordFormat(message.grid) then
if message.type == MESSAGE_TYPE.GET then
local gridData = map:getGrid(unpack(message.grid))
local replyMessage = newMessage(MESSAGE_TYPE.GET, message.ID, message.grid, gridData)
rednet.send(senderID, replyMessage, MAP_SHARE_PROTOCOL)
elseif message.type == MESSAGE_TYPE.SET then
if not receivedMessages[message.ID] then
updateMap(message.data, unpack(message.grid))
receivedMessages[message.ID] = true
receivedMessageTimeouts[os.startTimer(15)] = message.ID
end
local replyMessage = newMessage(MESSAGE_TYPE.SET, message.ID, message.grid, true)
rednet.send(senderID, replyMessage, MAP_SHARE_PROTOCOL)
end
end
end
end
--===== USER INTERFACE =====--
local function control()
while true do
local event, key = os.pullEvent("key")
if key == keys.backspace then
break
end
end
end
parallel.waitForAny(main, clearOldMessages, control)
rednet.unhost(MAP_SHARE_PROTOCOL)

117
maps/remoteMap_viewer Normal file
View File

@@ -0,0 +1,117 @@
local tArgs = {...}
local function printUsage()
print("Usage: viewRemoteMap <(string) mapName> <(int) x-coord> <(int) y-coord> <(int) z-coord>")
print("mapName must be the complete file path")
end
if not remoteMap then
if not os.loadAPI("remoteMap") then
error("Could not load remoteMap API")
end
end
for _, side in ipairs(redstone.getSides()) do
if peripheral.getType(side) == "modem" then
rednet.open(side)
end
end
if type(tArgs[1]) ~= "string" then
error("string expected for map name")
end
local map = remoteMap.new(tArgs[1], 5)
local startX, startY, startZ
if #tArgs == 4 then
for i = 2, 4 do
local num = tArgs[i]
if not tonumber(num) or num % 1 ~= 0 then
printUsage()
return
end
end
startX = tArgs[2]
startY = tArgs[3]
startZ = tArgs[4]
end
term.setCursorBlink(false)
term.setBackgroundColour(colours.black)
local cont = true
local width, height = term.getSize()
local currW, currH = 1, 1
term.setCursorBlink(true)
term.setTextColour(colours.red)
local redraw = true
while cont do
if redraw then
map:check()
term.setBackgroundColour(colours.black)
term.clear()
term.setCursorPos(1, height)
term.clearLine()
term.write(tostring(startX + currW - 1)..","..tostring(startY)..","..tostring(startZ + currH - 1))
term.write(" -- ")
term.write(tostring(math.floor( (startX + currW - 1)/16 )))
term.write(",")
term.write(tostring(math.floor( (startY)/16 )))
term.write(",")
term.write(tostring(math.floor( (startZ + currH - 1)/16 )))
for x = 1, width do
for z = 1, height - 1 do
local value = map:get(vector.new(startX + x - 1, startY, startZ + z - 1))
if value then
term.setBackgroundColour(colours.white)
term.setCursorPos(x, z)
term.write(string.sub(value, 1, 1))
end
end
end
term.setCursorPos(currW, currH)
redraw = false
end
local event = {os.pullEvent()}
if event[1] == "key" then
local key = event[2]
if key == keys.up then
startZ = startZ - 1
elseif key == keys.down then
startZ = startZ + 1
elseif key == keys.left then
startX = startX - 1
elseif key == keys.right then
startX = startX + 1
elseif key == keys.numPadAdd then
startY = startY + 1
elseif key == keys.numPadSubtract then
startY = startY - 1
elseif key == keys.backspace then
cont = false
end
redraw = true
elseif event[1] == "mouse_click" then
if event[4] < height then
currW, currH = event[3], event[4]
term.setBackgroundColour(colours.black)
term.setCursorPos(1, height)
term.clearLine()
term.write(tostring(startX + currW - 1)..","..tostring(startY)..","..tostring(startZ + currH - 1))
term.write(" -- ")
term.write(tostring(math.floor( (startX + currW - 1)/16 )))
term.write(",")
term.write(tostring(math.floor( (startY)/16 )))
term.write(",")
term.write(tostring(math.floor( (startZ + currH - 1)/16 )))
term.setCursorPos(currW, currH)
end
elseif event[1] == "term_resize" then
width, height = term.getSize()
redraw = true
end
end
term.setBackgroundColour(colours.black)
term.setCursorPos(1, 1)
term.clear()

222
maps/tinyMap Normal file
View File

@@ -0,0 +1,222 @@
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 = {
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)
for gX, YZmap in pairs(self.map) do
for gY, Zmap in pairs(YZmap) do
for gZ, grid in pairs(Zmap) do
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
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", 2)
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", 2)
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", 2)
end
tMap.mapDir = mapDir
else
error("new: directory must be string", 2)
end
tMap.map = {}
setmetatable(tMap, mapMetatable)
return tMap
end

128
maps/viewMap Normal file
View File

@@ -0,0 +1,128 @@
local tArgs = {...}
local function printUsage()
print("Usages:")
print("viewMap <(string) mapAPIPath> <(string) mapName>")
print("viewMap <(string) mapAPIPath> <(string) mapName> <(int) x-coord> <(int) y-coord> <(int) z-coord>")
print("mapName must be the complete file path")
end
local mapAPIPath = tArgs[1]
if not mapAPIPath or type(mapAPIPath) ~= "string" or not fs.exists(mapAPIPath) or fs.isDir(mapAPIPath) then
error("invalid mapAPIPath: "..tostring(mapAPIPath))
end
local mapAPI = fs.getName(mapAPIPath)
if not _G[mapAPI] then
if not os.loadAPI(mapAPIPath) then
error("could not load mapAPI: "..tostring(mapAPIPath))
end
end
mapAPI = _G[mapAPI]
local map
local mapName = tArgs[2]
if mapName and type(mapName) == "string" and fs.exists(mapName) and fs.isDir(mapName) then
map = mapAPI.new(mapName)
if not map then
error("could not load map at "..mapName)
end
else
printUsage()
end
local startX, startY, startZ
if #tArgs == 5 then
for i = 3, 5 do
local num = tArgs[i]
if not tonumber(num) or num % 1 ~= 0 then
printUsage()
return
end
end
startX = tArgs[3]
startY = tArgs[4]
startZ = tArgs[5]
end
if not startX then
map:loadAll()
--find any point on map that isn't empty
end
term.setCursorBlink(false)
term.setBackgroundColour(colours.black)
local cont = true
local width, height = term.getSize()
local currW, currH = 1, 1
term.setCursorBlink(true)
term.setTextColour(colours.red)
local redraw = true
while cont do
if redraw then
term.setBackgroundColour(colours.black)
term.clear()
term.setCursorPos(1, height)
term.clearLine()
term.write(tostring(startX + currW - 1)..","..tostring(startY)..","..tostring(startZ + currH - 1))
term.write(" -- ")
term.write(tostring(math.floor( (startX + currW - 1)/16 )))
term.write(",")
term.write(tostring(math.floor( (startY)/16 )))
term.write(",")
term.write(tostring(math.floor( (startZ + currH - 1)/16 )))
for x = 1, width do
for z = 1, height - 1 do
local value = map:get(vector.new(startX + x - 1, startY, startZ + z - 1))
if value then
term.setBackgroundColour(colours.white)
term.setCursorPos(x, z)
term.write(string.sub(value, 1, 1))
end
end
end
term.setCursorPos(currW, currH)
redraw = false
end
local event = {os.pullEvent()}
if event[1] == "key" then
local key = event[2]
if key == keys.up then
startZ = startZ - 1
elseif key == keys.down then
startZ = startZ + 1
elseif key == keys.left then
startX = startX - 1
elseif key == keys.right then
startX = startX + 1
elseif key == keys.numPadAdd then
startY = startY + 1
elseif key == keys.numPadSubtract then
startY = startY - 1
elseif key == keys.backspace then
cont = false
end
redraw = true
elseif event[1] == "mouse_click" then
if event[4] < height then
currW, currH = event[3], event[4]
term.setCursorPos(1, height)
term.clearLine()
term.write(tostring(startX + currW - 1)..","..tostring(startY)..","..tostring(startZ + currH - 1))
term.write(" -- ")
term.write(tostring(math.floor( (startX + currW - 1)/16 )))
term.write(",")
term.write(tostring(math.floor( (startY)/16 )))
term.write(",")
term.write(tostring(math.floor( (startZ + currH - 1)/16 )))
term.setCursorPos(currW, currH)
end
elseif event[1] == "term_resize" then
width, height = term.getSize()
redraw = true
end
end
term.setBackgroundColour(colours.black)
term.setCursorPos(1, 1)
term.clear()