From cef3cdf03d600751bdbf72b2119867dac5afbe0f Mon Sep 17 00:00:00 2001 From: MayaTheShy Date: Fri, 20 Feb 2026 03:54:52 -0500 Subject: [PATCH] refactor: update turtle script for improved server-driven functionality and code organization --- turtle.lua | 795 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 549 insertions(+), 246 deletions(-) diff --git a/turtle.lua b/turtle.lua index a896def..f3db946 100644 --- a/turtle.lua +++ b/turtle.lua @@ -1,306 +1,609 @@ --- Advanced Autonomous Mining Turtle v4 (Eval/Response Protocol) --- Features: Server-driven state machine, UUID command correlation, --- GPS tracking, local fallback intelligence, block scanning +-- Server-Driven Turtle v5 (Pure Eval Protocol)-- Advanced Autonomous Mining Turtle v4 (Eval/Response Protocol) + +-- All behavior is driven by the server via eval commands.-- Features: Server-driven state machine, UUID command correlation, + +-- This script only handles: eval execution, status broadcasting,-- GPS tracking, local fallback intelligence, block scanning + +-- GPS tracking, inventory/peripheral events. local CHANNEL_RECEIVE = 100 -local CHANNEL_SEND = 101 + +local CHANNEL_RECEIVE = 100local CHANNEL_SEND = 101 + +local CHANNEL_SEND = 101local STATUS_CHANNEL = 102 + local STATUS_CHANNEL = 102 -- State tracking (lightweight - server drives behavior via eval) -local state = { - mode = "idle", - position = nil, - homePosition = nil, - fuel = 0, - inventory = {}, - facing = 0, - lastStatusUpdate = 0, - evalSupported = true, + +-- State tracking (lightweight - server drives everything)local state = { + +local state = { mode = "idle", + + position = nil, position = nil, + + homePosition = nil, homePosition = nil, + + fuel = 0, fuel = 0, + + inventory = {}, inventory = {}, + + facing = 0, facing = 0, + + lastStatusUpdate = 0, lastStatusUpdate = 0, + +} evalSupported = true, + } -- Configuration -local config = { - statusUpdateInterval = 5, - valuableBlocks = { - ["minecraft:coal_ore"] = 1, - ["minecraft:iron_ore"] = 2, - ["minecraft:gold_ore"] = 3, - ["minecraft:diamond_ore"] = 5, - ["minecraft:emerald_ore"] = 5, - ["minecraft:redstone_ore"] = 2, - ["minecraft:lapis_ore"] = 2, - ["minecraft:deepslate_coal_ore"] = 1, - ["minecraft:deepslate_iron_ore"] = 2, - ["minecraft:deepslate_gold_ore"] = 3, - ["minecraft:deepslate_diamond_ore"] = 5, - ["minecraft:deepslate_emerald_ore"] = 5, - ["minecraft:deepslate_redstone_ore"] = 2, - ["minecraft:deepslate_lapis_ore"] = 2, - } -} --- Check for modem -local modem = peripheral.find("modem") -if not modem then - error("No wireless modem found!") -end -modem.open(CHANNEL_RECEIVE) +local config = {-- Configuration + + statusUpdateInterval = 5,local config = { + +} statusUpdateInterval = 5, + + valuableBlocks = { + +-- Check for modem ["minecraft:coal_ore"] = 1, + +local modem = peripheral.find("modem") ["minecraft:iron_ore"] = 2, + +if not modem then ["minecraft:gold_ore"] = 3, + + error("No wireless modem found!") ["minecraft:diamond_ore"] = 5, + +end ["minecraft:emerald_ore"] = 5, + +modem.open(CHANNEL_RECEIVE) ["minecraft:redstone_ore"] = 2, + +print("Server-Driven Turtle v5 (Pure Eval Protocol)") ["minecraft:lapis_ore"] = 2, + +print("ID: " .. os.getComputerID()) ["minecraft:deepslate_coal_ore"] = 1, + +print("Modem opened on channel " .. CHANNEL_RECEIVE) ["minecraft:deepslate_iron_ore"] = 2, + + ["minecraft:deepslate_gold_ore"] = 3, + +-- Store facing in global for eval access ["minecraft:deepslate_diamond_ore"] = 5, + +_G._turtleFacing = 0 ["minecraft:deepslate_emerald_ore"] = 5, + + ["minecraft:deepslate_redstone_ore"] = 2, + +-- ========== GPS Functions ========== ["minecraft:deepslate_lapis_ore"] = 2, + + } + +local function updatePosition()} + + local x, y, z = gps.locate(2) + + if x then-- Check for modem + + state.position = {x = math.floor(x), y = math.floor(y), z = math.floor(z)}local modem = peripheral.find("modem") + + return trueif not modem then + + end error("No wireless modem found!") + + return falseend + +endmodem.open(CHANNEL_RECEIVE) + print("Advanced Mining Turtle v4 (Eval Protocol)") -print("ID: " .. os.getComputerID()) + +-- ========== Fuel & Inventory ==========print("ID: " .. os.getComputerID()) + print("Modem opened on channel " .. CHANNEL_RECEIVE) --- Store facing in global for eval access -_G._turtleFacing = 0 +local function updateFuel() + + state.fuel = turtle.getFuelLevel()-- Store facing in global for eval access + + return state.fuel_G._turtleFacing = 0 + +end -- ========== GPS Functions ========== -local function updatePosition() - local x, y, z = gps.locate(2) - if x then - state.position = {x = math.floor(x), y = math.floor(y), z = math.floor(z)} - return true +local function updateInventory() + + state.inventory = {}local function updatePosition() + + for slot = 1, 16 do local x, y, z = gps.locate(2) + + local item = turtle.getItemDetail(slot) if x then + + if item then state.position = {x = math.floor(x), y = math.floor(y), z = math.floor(z)} + + table.insert(state.inventory, { return true + + slot = slot, end + + name = item.name, return false + + count = item.countend + + }) + + end-- ========== Movement Functions (with position tracking) ========== + end - return false -end --- ========== Movement Functions (with position tracking) ========== +endlocal function updateFacingAfterTurn(right) -local function updateFacingAfterTurn(right) if right then - state.facing = (state.facing + 1) % 4 + +-- ========== Status Broadcasting ========== state.facing = (state.facing + 1) % 4 + else - state.facing = (state.facing - 1) % 4 - end - _G._turtleFacing = state.facing + +local function broadcastStatus() state.facing = (state.facing - 1) % 4 + + updateFuel() end + + updateInventory() _G._turtleFacing = state.facing + end -local function smartForward() + local surroundings = {} + + local hasBlock, datalocal function smartForward() + local success = turtle.forward() - if success and state.position then - if state.facing == 0 then state.position.z = state.position.z - 1 + + hasBlock, data = turtle.inspect() if success and state.position then + + if hasBlock then surroundings.forward = {name = data.name, metadata = data.metadata or 0} end if state.facing == 0 then state.position.z = state.position.z - 1 + elseif state.facing == 1 then state.position.x = state.position.x + 1 - elseif state.facing == 2 then state.position.z = state.position.z + 1 - elseif state.facing == 3 then state.position.x = state.position.x - 1 + + hasBlock, data = turtle.inspectUp() elseif state.facing == 2 then state.position.z = state.position.z + 1 + + if hasBlock then surroundings.up = {name = data.name, metadata = data.metadata or 0} end elseif state.facing == 3 then state.position.x = state.position.x - 1 + end - end - return success + + hasBlock, data = turtle.inspectDown() end + + if hasBlock then surroundings.down = {name = data.name, metadata = data.metadata or 0} end return success + end -local function smartBack() - local success = turtle.back() - if success and state.position then - if state.facing == 0 then state.position.z = state.position.z + 1 - elseif state.facing == 1 then state.position.x = state.position.x - 1 - elseif state.facing == 2 then state.position.z = state.position.z - 1 - elseif state.facing == 3 then state.position.x = state.position.x + 1 - end - end - return success -end + local statusPacket = { + + type = "status",local function smartBack() + + turtleID = os.getComputerID(), local success = turtle.back() + + position = state.position, if success and state.position then + + homePosition = state.homePosition, if state.facing == 0 then state.position.z = state.position.z + 1 + + fuel = state.fuel, elseif state.facing == 1 then state.position.x = state.position.x - 1 + + inventoryCount = #state.inventory, elseif state.facing == 2 then state.position.z = state.position.z - 1 + + inventory = state.inventory, elseif state.facing == 3 then state.position.x = state.position.x + 1 + + facing = state.facing, end + + surroundings = surroundings, end + + evalSupported = true, return success + + label = os.getComputerLabel(),end + + } local function smartUp() - local success = turtle.up() - if success and state.position then + + modem.transmit(STATUS_CHANNEL, CHANNEL_RECEIVE, statusPacket) local success = turtle.up() + +end if success and state.position then + state.position.y = state.position.y + 1 - end - return success -end -local function smartDown() - local success = turtle.down() - if success and state.position then - state.position.y = state.position.y - 1 - end - return success -end +-- ========== Eval/Response Protocol ========== end + + return success + +local function executeEval(uuid, code)end + + local fn, compileError = load(code, "eval", "t") + + if not fn thenlocal function smartDown() + + modem.transmit(CHANNEL_SEND, CHANNEL_RECEIVE, { local success = turtle.down() + + type = "eval_response", if success and state.position then + + uuid = uuid, state.position.y = state.position.y - 1 + + result = nil, end + + error = "Compile error: " .. tostring(compileError), return success + + turtleID = os.getComputerID()end + + }) + + returnlocal function smartTurnRight() + + end local success = turtle.turnRight() -local function smartTurnRight() - local success = turtle.turnRight() if success then updateFacingAfterTurn(true) end - return success -end -local function smartTurnLeft() - local success = turtle.turnLeft() - if success then updateFacingAfterTurn(false) end - return success -end + local results = table.pack(pcall(fn)) return success --- ========== Fuel & Inventory ========== + local ok = results[1]end -local function updateFuel() - state.fuel = turtle.getFuelLevel() - return state.fuel -end -local function tryRefuel() - for slot = 1, 16 do - turtle.select(slot) - if turtle.refuel(0) then - local count = turtle.getItemCount() - if count > 0 then - turtle.refuel(1) - return true - end - end - end - return false + + if ok thenlocal function smartTurnLeft() + + local returnVal local success = turtle.turnLeft() + + if results.n <= 2 then if success then updateFacingAfterTurn(false) end + + returnVal = results[2] return success + + elseend + + returnVal = {} + + for i = 2, results.n do-- ========== Fuel & Inventory ========== + + returnVal[i - 1] = results[i] + + endlocal function updateFuel() + + end state.fuel = turtle.getFuelLevel() + + modem.transmit(CHANNEL_SEND, CHANNEL_RECEIVE, { return state.fuel + + type = "eval_response",end + + uuid = uuid, + + result = returnVal,local function tryRefuel() + + error = nil, for slot = 1, 16 do + + turtleID = os.getComputerID() turtle.select(slot) + + }) if turtle.refuel(0) then + + else local count = turtle.getItemCount() + + modem.transmit(CHANNEL_SEND, CHANNEL_RECEIVE, { if count > 0 then + + type = "eval_response", turtle.refuel(1) + + uuid = uuid, return true + + result = nil, end + + error = "Runtime error: " .. tostring(results[2]), end + + turtleID = os.getComputerID() end + + }) return false + + endend + end local function updateInventory() - state.inventory = {} - for slot = 1, 16 do - local item = turtle.getItemDetail(slot) - if item then - table.insert(state.inventory, { - slot = slot, - name = item.name, - count = item.count - }) - end - end -end -local function inventoryFull() +-- ========== Message Processing ========== state.inventory = {} + for slot = 1, 16 do - if turtle.getItemCount(slot) == 0 then - return false - end + +local function processMessage(channel, message) local item = turtle.getItemDetail(slot) + + if type(message) ~= "table" then return end if item then + + local myID = os.getComputerID() table.insert(state.inventory, { + + if message.target and message.target ~= myID then return end slot = slot, + + name = item.name, + + if message.type == "eval" and message.uuid and message.code then count = item.count + + print("Eval: " .. message.uuid:sub(1, 8) .. "...") }) + + executeEval(message.uuid, message.code) end + end - return true -end + + elseif message.type == "home_position" and message.turtleID == myID thenend + + if message.homePosition then + + state.homePosition = message.homePositionlocal function inventoryFull() + + print("Home synced from server") for slot = 1, 16 do + + end if turtle.getItemCount(slot) == 0 then + + return false + + elseif message.type == "home_set_confirm" and message.turtleID == myID then end + + if message.homePosition then end + + state.homePosition = message.homePosition return true + + print("Home confirmed by server")end + + end -- ========== Block Scanning ========== -local function scanAllDirections() - if not state.position then return {} end - - local scannedBlocks = {} - local myID = os.getComputerID() - - local function addBlock(relX, relY, relZ, blockData) - if blockData and blockData.name then - table.insert(scannedBlocks, { - x = state.position.x + relX, - y = state.position.y + relY, - z = state.position.z + relZ, - name = blockData.name, - metadata = blockData.metadata or 0, - discoveredBy = myID - }) - end - end - - local hasBlock, data - hasBlock, data = turtle.inspectUp() - if hasBlock then addBlock(0, 1, 0, data) end - - hasBlock, data = turtle.inspectDown() - if hasBlock then addBlock(0, -1, 0, data) end - - local originalFacing = state.facing - for i = 0, 3 do - hasBlock, data = turtle.inspect() - if hasBlock then - local relX, relZ = 0, 0 - if state.facing == 0 then relZ = -1 - elseif state.facing == 1 then relX = 1 - elseif state.facing == 2 then relZ = 1 - elseif state.facing == 3 then relX = -1 - end - addBlock(relX, 0, relZ, data) - end - if i < 3 then smartTurnRight() end - end - - if state.facing ~= originalFacing then - smartTurnRight() - end - - return scannedBlocks -end + elseif message.type == "rename" and message.name then + + os.setComputerLabel(message.name)local function scanAllDirections() + + print("Renamed to: " .. message.name) if not state.position then return {} end + + broadcastStatus() + + end local scannedBlocks = {} + +end local myID = os.getComputerID() + + + +-- ========== Sync Home ========== local function addBlock(relX, relY, relZ, blockData) + + if blockData and blockData.name then + +local function syncHomeWithServer() table.insert(scannedBlocks, { + + modem.transmit(CHANNEL_SEND, CHANNEL_RECEIVE, { x = state.position.x + relX, + + type = "request_home", y = state.position.y + relY, + + turtleID = os.getComputerID() z = state.position.z + relZ, + + }) name = blockData.name, + + return true metadata = blockData.metadata or 0, + +end discoveredBy = myID + + }) + +-- ========== Initialization ========== end + + end + +print("Initializing...") + +local x, y, z = gps.locate(5) local hasBlock, data + +if x then hasBlock, data = turtle.inspectUp() + + state.position = {x = math.floor(x), y = math.floor(y), z = math.floor(z)} if hasBlock then addBlock(0, 1, 0, data) end + + print("GPS: OK - " .. state.position.x .. "," .. state.position.y .. "," .. state.position.z) + +else hasBlock, data = turtle.inspectDown() + + print("GPS: Not available") if hasBlock then addBlock(0, -1, 0, data) end + + state.position = nil + +end local originalFacing = state.facing + + for i = 0, 3 do + +syncHomeWithServer() hasBlock, data = turtle.inspect() + +updateFuel() if hasBlock then + +print("Ready! Turtle " .. os.getComputerID() .. " online (v5 server-driven)") local relX, relZ = 0, 0 + +broadcastStatus() if state.facing == 0 then relZ = -1 + + elseif state.facing == 1 then relX = 1 + +-- ========== Main Loop ========== elseif state.facing == 2 then relZ = 1 + + elseif state.facing == 3 then relX = -1 + +parallel.waitForAny( end + + function() addBlock(relX, 0, relZ, data) + + -- GPS retry loop end + + while true do if i < 3 then smartTurnRight() end + + if not state.position then end + + sleep(10) + + if updatePosition() then if state.facing ~= originalFacing then + + print("GPS acquired!") smartTurnRight() + + broadcastStatus() end + + end + + else return scannedBlocks + + sleep(30)end + + end + + endlocal function reportDiscoveredBlocks(blocks) + + end, if #blocks == 0 then return end -local function reportDiscoveredBlocks(blocks) - if #blocks == 0 then return end modem.transmit(CHANNEL_SEND, CHANNEL_RECEIVE, { - type = "blocks_discovered", - turtleID = os.getComputerID(), - blocks = blocks, - timestamp = os.epoch("utc") - }) -end + + function() type = "blocks_discovered", + + -- Status broadcast loop turtleID = os.getComputerID(), + + while true do blocks = blocks, + + sleep(config.statusUpdateInterval) timestamp = os.epoch("utc") + + broadcastStatus() }) + + endend + + end, -- ========== Status Broadcasting ========== -local function broadcastStatus() - updateFuel() - updateInventory() - - local surroundings = {} - local hasBlock, data - - hasBlock, data = turtle.inspect() + function() + + -- Command processing (eval protocol)local function broadcastStatus() + + while true do updateFuel() + + local event, side, channel, replyChannel, message = os.pullEvent("modem_message") updateInventory() + + if channel == CHANNEL_RECEIVE then + + processMessage(channel, message) local surroundings = {} + + end local hasBlock, data + + end + + end, hasBlock, data = turtle.inspect() + if hasBlock then surroundings.forward = {name = data.name, metadata = data.metadata or 0} end - - hasBlock, data = turtle.inspectUp() - if hasBlock then surroundings.up = {name = data.name, metadata = data.metadata or 0} end - - hasBlock, data = turtle.inspectDown() - if hasBlock then surroundings.down = {name = data.name, metadata = data.metadata or 0} end - - local statusPacket = { - type = "status", - turtleID = os.getComputerID(), - mode = state.mode, - position = state.position, - homePosition = state.homePosition, - fuel = state.fuel, - inventoryCount = #state.inventory, + + function() + + -- Inventory change events (real-time) hasBlock, data = turtle.inspectUp() + + while true do if hasBlock then surroundings.up = {name = data.name, metadata = data.metadata or 0} end + + os.pullEvent("turtle_inventory") + + local inventory = {} hasBlock, data = turtle.inspectDown() + + for i = 1, 16 do if hasBlock then surroundings.down = {name = data.name, metadata = data.metadata or 0} end + + local item = turtle.getItemDetail(i) + + if item then local statusPacket = { + + table.insert(inventory, { type = "status", + + slot = i, turtleID = os.getComputerID(), + + name = item.name, mode = state.mode, + + count = item.count position = state.position, + + }) homePosition = state.homePosition, + + end fuel = state.fuel, + + end inventoryCount = #state.inventory, + inventory = state.inventory, - facing = state.facing, - surroundings = surroundings, - evalSupported = true, - label = os.getComputerLabel(), - } - - modem.transmit(STATUS_CHANNEL, CHANNEL_RECEIVE, statusPacket) -end --- ========== Eval/Response Protocol ========== + modem.transmit(CHANNEL_SEND, CHANNEL_RECEIVE, { facing = state.facing, + + type = "inventory_update", surroundings = surroundings, + + turtleID = os.getComputerID(), evalSupported = true, + + inventory = inventory, label = os.getComputerLabel(), + + timestamp = os.epoch("utc") } + + }) + + end modem.transmit(STATUS_CHANNEL, CHANNEL_RECEIVE, statusPacket) + + end,end + + + + function()-- ========== Eval/Response Protocol ========== + + -- Peripheral attached events (real-time) + + while true dolocal function executeEval(uuid, code) + + os.pullEvent("peripheral") local fn, compileError = load(code, "eval", "t") + + sleep(0.1) if not fn then + + local peripherals = {} modem.transmit(CHANNEL_SEND, CHANNEL_RECEIVE, { + + local names = peripheral.getNames() type = "eval_response", + + for _, name in ipairs(names) do uuid = uuid, + + peripherals[name] = {types = {peripheral.getType(name)}} result = nil, + + end error = "Compile error: " .. tostring(compileError), -local function executeEval(uuid, code) - local fn, compileError = load(code, "eval", "t") - if not fn then - modem.transmit(CHANNEL_SEND, CHANNEL_RECEIVE, { - type = "eval_response", - uuid = uuid, - result = nil, - error = "Compile error: " .. tostring(compileError), turtleID = os.getComputerID() - }) - return - end - - local results = table.pack(pcall(fn)) - local ok = results[1] - - if ok then + + modem.transmit(CHANNEL_SEND, CHANNEL_RECEIVE, { }) + + type = "peripheral_attached", return + + turtleID = os.getComputerID(), end + + peripherals = peripherals, + + timestamp = os.epoch("utc") local results = table.pack(pcall(fn)) + + }) local ok = results[1] + + end + + end, if ok then + local returnVal - if results.n <= 2 then - -- Single return value - returnVal = results[2] - else - -- Multiple return values - pack into array - returnVal = {} - for i = 2, results.n do - returnVal[i - 1] = results[i] - end - end - modem.transmit(CHANNEL_SEND, CHANNEL_RECEIVE, { - type = "eval_response", - uuid = uuid, - result = returnVal, - error = nil, + + function() if results.n <= 2 then + + -- Peripheral detached events (real-time) -- Single return value + + while true do returnVal = results[2] + + local _, side = os.pullEvent("peripheral_detach") else + + if not peripheral.isPresent(side) then -- Multiple return values - pack into array + + modem.transmit(CHANNEL_SEND, CHANNEL_RECEIVE, { returnVal = {} + + type = "peripheral_detached", for i = 2, results.n do + + turtleID = os.getComputerID(), returnVal[i - 1] = results[i] + + side = side, end + + timestamp = os.epoch("utc") end + + }) modem.transmit(CHANNEL_SEND, CHANNEL_RECEIVE, { + + end type = "eval_response", + + end uuid = uuid, + + end result = returnVal, + +) error = nil, + turtleID = os.getComputerID() }) else