local component = require("component") local event = require("event") local fs = require("filesystem") local ser = require("serialization") local modem = component.modem if component.isAvailable("tunnel") then tunnel = component.tunnel end local bPort = 20 local ePort = 21 modem.open(bPort) modem.open(ePort) local localAddress = modem.address local nodeList = {} local relays = {} local relayNodes = {} -- LOG FUNCTIONS -- local function log(m) print("Log:", m) end local function msg(m) print("Message:", m) end local function err(m) print("Error:", m) end -- LEN FUNCTION -- (Gives the length of a table) function len(table) local count = 0 for _, _ in pairs(table) do count = count + 1 end return count end -- READ FUNCTION -- (Read the config file) local function read() if fs.exists("/home/.eConfig.cfg") then local file = io.open(".eConfig.cfg") local stream = file:read() file:close() return ser.unserialize(stream) else err("No config file.") return false end end -- WRITE FUNCTION -- (Write in the config file) local function write() local file = io.open(".eConfig.cfg", "w") file:write(ser.serialize(configdata)) file:close() end -- WHO FUNCTION -- (Transmit identity of the router) function who(to) modem.send(to, bPort, "WHO", localAlias) end -- SEND NODES FUNCTION -- (Send the nodelist) function sendNodes(to) local fulltable = {} for k, v in pairs(nodeList) do fulltable[k] = v end for relayID, nodes in pairs(relayNodes) do if relayID ~= to then for k, v in pairs(nodes) do fulltable[k] = v end end end modem.send(relays[to], ePort, "LST", localAlias, ser.serialize(fulltable)) log("Send nodes to "..to) end -- GET NODES FUNCTION -- (Receive a nodelist) function getNodes(from, nodes) log("Nodes received from relay "..from) relayNodes[from] = ser.unserialize(nodes) for k, v in pairs(relays) do if k ~= from then log("Relay nodes to "..k) sendNodes(k) end end end -- CONNECT FUNCTION -- (Connect a node to this router) function connect(addr, alias) local found = false for relayID, nodes in pairs(relayNodes) do if relayNodes[relayID][alias] or nodeList[alias] then -- If a node with the same alias is already connected found = true if nodeList[alias] == addr then modem.send(addr, bPort, "CONNECT") log("Crashed node "..alias.." reconnecting") else modem.send(addr, bPort, "DPL") err("Attempted duplicate alias connection.") end break end end if found == false then nodeList[alias] = addr modem.send(addr, bPort, "CONNECT") log("Node "..alias.." connected.") for k, v in pairs(relays) do sendNodes(k) end end end -- SERVERCONNECT FUNCTION -- (Connect this router to other routers) function serverconnect(addr, alias) relays[alias] = addr if not configdata["relays"][alias] then configdata["relays"][alias] = addr log("Get connection from relay "..alias) write() end sendNodes(alias) end -- TRANSMIT FUNCTION -- (Transmit message from node to node) function transmit(flag, from, to, data) local isFound = false for k, v in pairs(nodeList) do if to == k then modem.send(nodeList[to], ePort, flag, from, to, data) log("LOCAL "..flag.." from "..from.." to "..to) isFound = true end end if isFound == false then for relayID, nodes in pairs(relayNodes) do for k, v in pairs(nodes) do if to == k then modem.send(relays[relayID], ePort, flag, from, to, data) log("DISTANT "..flag.." from "..from.." to "..to) isFound = true end if isFound then break end end if isFound then break end end end if isFound == false then modem.send(nodeList[from], ePort, "PNF") err("Tried to found node "..to.." but failed") end end -- DISCONNECT FUNCTION -- (Disconnect a node from this router) function disconnect(addr, alias) if nodeList[alias] then nodeList[alias] = nil modem.send(addr, ePort, "DISCONNECT") log("Node "..alias.." disconnected.") for k, v in pairs(relays) do sendNodes(k) end else transmit("DISCONNECT", "0", alias) end end -- TUNNEL INIT FUNCTION -- (Initialize a linked tunnel connection) function inittunnel() if component.isAvailable("tunnel") then local notConnected = true while notConnected do tunnel.send("TUNNEL", ser.serialize(configdata["relays"])) local _, _, _, _, _, flag, linkedRelays = event.pull("modem_message") if flag == "TUNNEL" then tunnel.send("TUNNEL", ser.serialize(configdata["relays"])) notConnected = false for k, v in pairs(ser.unserialize(linkedRelays)) do relays[k] = "0000" end end end end end -- INITIALIZATION -- configdata = read() if configdata then inittunnel() localAlias = configdata["hostname"] for k, v in pairs(configdata["relays"]) do modem.send(v, ePort, "SERVERCONNECT", localAddress, localAlias) log("Send connect to relay "..k) serverconnect(v, k) end -- MAIN LOOP -- while true do local _, _, addr, port, d, flag, from, to, data = event.pull("modem_message") if flag == "WHO" then who(addr, d) elseif flag == "CONNECT" then connect(from, to) -- "to" here is the alias elseif flag == "SERVERCONNECT" then serverconnect(from, to) -- "to" here is the alias elseif flag == "LST" then getNodes(from, to) -- "to" here is the serialized nodelist elseif flag == "DISCONNECT" then disconnect(from, to) -- "to" here is the alias elseif flag ~= "TUNNEL" then transmit(flag, from, to, data) end if component.isAvailable("tunnel") and (port ~= 0) then tunnel.send(flag, from, to, data) end end end