From d9efbc7536faa2d83655bfc1153201f99af4a6f9 Mon Sep 17 00:00:00 2001 From: neru Date: Tue, 6 May 2025 18:07:46 -0300 Subject: [PATCH] upload tutel --- lua/CC Tweaked/tutel/tutel-client.lua | 208 ++++++++++++++++++++++++++ lua/CC Tweaked/tutel/tutel-host.lua | 184 +++++++++++++++++++++++ 2 files changed, 392 insertions(+) create mode 100644 lua/CC Tweaked/tutel/tutel-client.lua create mode 100644 lua/CC Tweaked/tutel/tutel-host.lua diff --git a/lua/CC Tweaked/tutel/tutel-client.lua b/lua/CC Tweaked/tutel/tutel-client.lua new file mode 100644 index 0000000..0634699 --- /dev/null +++ b/lua/CC Tweaked/tutel/tutel-client.lua @@ -0,0 +1,208 @@ +--[[ + tutel client + + __ + .,-;-;-,. /'_\ + _/_/_/_|_\_\) / + '-<_><_><_><_>=/\ + `/_/====/_/-'\_\ + "" "" "" +]] + +--[[ + network variables +]] +local HOST_CHANNEL +local TURTLE_CHANNEL + +local MSG_HEADER = 0x747574656C + +local modem = peripheral.find("modem") or error("No modems found", 0) +modem.open(0) -- general port used for scanning + +--[[ + 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 + +--[[ + helper networking functions +]] +function sendToHost(name, msgData) + local message = { + ["header"] = MSG_HEADER, + ["name"] = name, + ["data"] = msgData or {}, + ["id"] = os.getComputerID() + } + + modem.transmit(HOST_CHANNEL, TURTLE_CHANNEL or 0, message) +end + +--[[ + command handling +]] +local commands = {} +function declareCommandHandler(name, handler, requiredKeys) + commands[name] = { + ["keys"] = requiredKeys or {}, + ["handler"] = handler + } +end + +-- scan +function handleScan(keys) + -- to-do: check and maybe inform if turtle is already linked to another host + + HOST_CHANNEL = keys.hostChannel + modem.close(0) + + log.info("received scan, host channel assigned to %d, replying...", keys.hostChannel) + + local x, y, z = gps.locate() + + sendToHost("SCAN", { + ["positionX"] = x, + ["positionY"] = y, + ["positionZ"] = z, + ["fuel"] = turtle.getFuelLevel() + }) + + -- HOST_CHANNEL = keys.hostChannel + -- REPLY_CHANNEL = keys.replyChannel + -- log.info("Received scan, HOST_CHANNEL: %d - REPLY_CHANNEL: %d", HOST_CHANNEL, REPLY_CHANNEL) +end + +declareCommandHandler("SCAN", handleScan, { ["hostChannel"] = "number" }) + +-- register +function handleRegister(keys) + log.info("received register for id:", keys.id) + + if (keys.id ~= os.getComputerID()) then return end -- message was not meant for this computer + TURTLE_CHANNEL = keys.turtleChannel + + log.info("received register, host assigned channel: %d", keys.turtleChannel) + modem.open(keys.turtleChannel) +end + +declareCommandHandler("REGISTER", handleRegister, { ["id"] = "number", ["turtleChannel"] = "number" }) + +-- status +function handleStatus(keys) + +end + +declareCommandHandler("STATUS", handleStatus) + +-- goto +function handleGoTo(keys) + +end + +declareCommandHandler("GOTO", handleGoTo, { ["destX"] = "number", ["destY"] = "number", ["destZ"] = "number" }) + +-- mine area +function handleMineArea(keys) + +end + +declareCommandHandler("MINE-AREA", handleGoTo, + { ["startX"] = "number", ["startY"] = "number", ["destX"] = "number", ["destZ"] = "number" }) + +-- abort +function handleAbort() + +end + +declareCommandHandler("ABORT", handleAbort) + +--[[ + main +]] +function pollAndProcessMessages() + local eventType, side, channel, replyChannel, message, dist = os.pullEvent("modem_message") + + -- ignore messages not from host, HOST_CHANNEL and REPLY_CHANNEL will get assigned after receiving SCAN on channel 0 + if TURTLE_CHANNEL and channel ~= TURTLE_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 handling + log.info("processed command (%s)", message.name) +end + +log.general("tutel client init") + +while true do + pollAndProcessMessages() +end diff --git a/lua/CC Tweaked/tutel/tutel-host.lua b/lua/CC Tweaked/tutel/tutel-host.lua new file mode 100644 index 0000000..da54dbf --- /dev/null +++ b/lua/CC Tweaked/tutel/tutel-host.lua @@ -0,0 +1,184 @@ +--[[ + 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