--[[ tutel host __ .,-;-;-,. /'_\ _/_/_/_|_\_\) / '-<_><_><_><_>=/\ `/_/====/_/-'\_\ "" "" "" ]] --[[ network variables ]] local HOST_CHANNEL = 1337 local TURTLE_CHANNEL_BASE = 1338 local MSG_HEADER = 0x747574656C local modem = peripheral.find("modem") or error("No modems found", 0) modem.open(HOST_CHANNEL) --[[ logging ]] local logTypes = { ["general"] = { ["BG"] = colours.green, ["FG"] = colours.white }, ["info"] = { ["BG"] = colours.blue, ["FG"] = colours.white }, ["warning"] = { ["BG"] = colours.orange, ["FG"] = colours.black, ["AffectContent"] = true }, ["error"] = { ["BG"] = colours.red, ["FG"] = colours.black, ["AffectContent"] = true } } local log = {} for typeName, format in pairs(logTypes) do log[typeName] = function(fmt, ...) local prevColourBg = term.getBackgroundColour() local prevColourFg = term.getTextColour() term.setBackgroundColour(format["BG"]) term.setTextColour(format["FG"]) io.write(("[%s @ line %d]"):format(typeName, debug.getinfo(2, "l").currentline)) if format["AffectContent"] then io.write(" ", string.format(fmt, ...), "\n") term.setBackgroundColour(prevColourBg) term.setTextColour(prevColourFg) else term.setBackgroundColour(prevColourBg) term.setTextColour(prevColourFg) io.write(" ", string.format(fmt, ...), "\n") end end end --[[ turtle tracking ]] local turtles = {} function registerTurtle(pos, fuel, id) local channel = TURTLE_CHANNEL_BASE + #turtles local turt = { ["channel"] = channel, ["index"] = #turtles, ["position"] = pos, ["fuel"] = fuel, ["status"] = "IDLE", ["id"] = id } table.insert(turtles, turt) return turt end --[[ helper networking functions ]] function sendToAll(name, msgData) local message = { ["header"] = MSG_HEADER, ["name"] = name, ["data"] = msgData or {} } modem.transmit(0, 0, message) end --[[ command handling ]] local commands = {} function declareCommandHandler(name, handler, requiredKeys) commands[name] = { ["keys"] = requiredKeys or {}, ["handler"] = handler } end -- scan function handleScan(keys, id) local turt = registerTurtle({ keys.positionX, keys.positionY, keys.positionZ }, keys.fuel, id) log.info("Received turtle info (pos: %d, %d, %d - fuel: %d - channel: %d - id: %d)", turt.position[1], turt.position[2], turt.position[3], turt.fuel, turt.channel, id) sendToAll("REGISTER", { ["id"] = id, ["turtleChannel"] = turt.channel }) end declareCommandHandler("SCAN", handleScan, { ["positionX"] = "number", ["positionY"] = "number", ["positionZ"] = "number", ["fuel"] = "number" }) --[[ main ]] function pollAndProcessMessages() local eventType, side, channel, replyChannel, message, dist = os.pullEvent("modem_message") -- to-do: check if reply channel is valid turtle channel if channel ~= HOST_CHANNEL then return end -- message validation if type(message) ~= "table" then return end if not message.header or message.header ~= MSG_HEADER then return end if not message.data then message.data = {} end -- command validation if not message.name then log.warning("received nameless message") return end if not commands[message.name] then log.error("unhandled message (%s)", message.name) return end local cmd = commands[message.name] for i, v in pairs(cmd.keys) do if type(message.data[i]) ~= v and v ~= "optional" then log.error("command %s requires key %s of type %s (was %s)", message.name, i, v, type(message[i])) return end end cmd.handler(message.data, message.id) -- message handling log.info("received command (%s)", message.name) end log.general("tutel host init") -- transmit scan sendToAll("SCAN", { ["hostChannel"] = HOST_CHANNEL }) log.info("broadcasted scan message") while true do pollAndProcessMessages() end