--[[ 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 --[[ event handling ]] local eventListeners = {} local function registerEventListener(name, listener) if not eventListeners[name] then eventListeners[name] = {} end table.insert(eventListeners[name], listener) end local function dispatchEvents(event, ...) log.info("dispatching event: %s", event) local eventTbl = eventListeners[event] if not eventTbl then return end for i = 0, #eventTbl, 1 do eventTbl[i](...) end 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" }) --[[ modem message handler ]] local function handleMessages(side, channel, replyChannel, message, dist) if channel ~= HOST_CHANNEL then return end -- msg 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 -- cmd validation if not message.name then log.warning("received nameless command?") return end if not commands[message.name] then log.error("unhandled command (%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 execution local success, err = pcall(cmd.handler, message.data, message.id) if not success then log.error("error occurred while executing command %s: %s", message.name, err) end end registerEventListener("modem_message", handleMessages) --[[ main ]] log.general("tutel host init") -- transmit scan log.info("broadcasting SCAN") sendToAll("SCAN", { ["hostChannel"] = HOST_CHANNEL }) --[[ loop functions ]] function updateScreen() local w, h = term.getSize() local function drawTextCentered(text) local x = (w / 2) - (text:len() / 2) local _, curY = term.getCursorPos() term.setCursorPos(x, curY) term.write(text) end term.setBackgroundColor(colors.black) term.clear() term.setCursorPos(1, 1) term.setBackgroundColor(colors.lightGray) term.setTextColour(colors.black) term.clearLine() drawTextCentered("tutel host controller") end function pollEvents() local eventData = { os.pullEvent() } local eventName = eventData[1] table.remove(eventData, 1) dispatchEvents(eventName, table.unpack(eventData)) end while true do pollEvents() end