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