Add modem-based crafting functionality: implement command handling and inventory management

This commit is contained in:
MayaTheShy
2026-03-22 01:28:46 -04:00
parent 582c92f090
commit cc6b1e999a

View File

@@ -1,20 +1,26 @@
-- Crafting Turtle Script
-- Run this on the crafting turtle (turtle with crafting table).
-- Polls its own crafting grid slots and auto-crafts when items appear.
-- No modem required — works via the wired network inventory access.
-- Listens for craft commands from the master via wired modem.
-- Pulls ingredients from chests, crafts, and pushes results back.
-- Requires a wired modem attached to the turtle.
-------------------------------------------------
-- Configuration
-------------------------------------------------
-- Modem channels (must match inventoryManager.lua)
local CRAFT_CHANNEL = 4203 -- master -> turtle (craft requests)
local CRAFT_REPLY_CHANNEL = 4204 -- turtle -> master (craft results)
-- Crafting grid slots in a turtle's 4x4 inventory
local CRAFT_SLOTS = {1, 2, 3, 5, 6, 7, 9, 10, 11}
-- How long to wait after detecting items before crafting,
-- giving the master time to finish placing all ingredients.
local SETTLE_DELAY = 1.0
-- Polling interval (seconds)
local POLL_INTERVAL = 0.5
-------------------------------------------------
-- Setup
-------------------------------------------------
print("=================================")
print(" Crafting Turtle (Auto-Craft)")
print(" Crafting Turtle (Modem-Based)")
print("=================================")
print("")
@@ -25,17 +31,72 @@ if not turtle or not turtle.craft then
return
end
print("Polling crafting slots for items...")
print("Ready. Items placed in grid will be auto-crafted.")
print("")
-- Find wired modem and get our network name
local modem = nil
local modemSide = nil
local selfName = nil
local function hasCraftingItems()
for _, slot in ipairs(CRAFT_SLOTS) do
if turtle.getItemCount(slot) > 0 then
return true
for _, side in ipairs({"top", "bottom", "left", "right", "front", "back"}) do
if peripheral.getType(side) == "modem" then
local m = peripheral.wrap(side)
if m.getNameLocal then
local name = m.getNameLocal()
if name then
modem = m
modemSide = side
selfName = name
break
end
end
end
return false
end
if not modem or not selfName then
print("[ERR] No wired modem found!")
print(" Attach a wired modem to the turtle")
print(" and connect it to the network.")
return
end
print("[OK] Modem: " .. modemSide)
print("[OK] Network name: " .. selfName)
-- Open channel for receiving craft commands
modem.open(CRAFT_CHANNEL)
print("[OK] Listening on channel " .. CRAFT_CHANNEL)
-- Wrap our own inventory peripheral for pullItems/pushItems
local selfInv = peripheral.wrap(selfName)
if not selfInv then
print("[ERR] Cannot wrap own peripheral: " .. selfName)
print(" Make sure the wired modem is connected.")
return
end
print("[OK] Self-inventory peripheral ready")
print("")
print("Waiting for craft commands from master...")
print("")
-------------------------------------------------
-- Helpers
-------------------------------------------------
local function clearInventory(chests)
-- Push all items from all 16 turtle slots back to chests
local cleared = 0
for slot = 1, 16 do
if turtle.getItemCount(slot) > 0 then
for _, chestName in ipairs(chests) do
local ok, n = pcall(selfInv.pushItems, chestName, slot)
if ok and n and n > 0 then
cleared = cleared + n
break
end
end
end
end
return cleared
end
local function describeGrid()
@@ -49,40 +110,150 @@ local function describeGrid()
return table.concat(parts, "\n")
end
while true do
if hasCraftingItems() then
-- Wait for the master to finish placing all ingredients
print("[CRAFT] Items detected in grid, waiting " .. SETTLE_DELAY .. "s...")
sleep(SETTLE_DELAY)
-------------------------------------------------
-- Craft command handler
-------------------------------------------------
-- Check again — items might have been pulled back (abort)
if hasCraftingItems() then
print("[CRAFT] Grid contents:")
print(describeGrid())
print("[CRAFT] Attempting craft...")
local function handleCraftCommand(message)
-- message format from master:
-- {
-- type = "craft_request",
-- recipeIdx = <number>,
-- output = <string>, -- expected output item name
-- slots = { [turtleSlot] = { chestName=..., chestSlot=..., itemName=..., count=... }, ... },
-- returnChests = { "chest_0", "chest_1", ... },
-- }
local ok, err = turtle.craft()
local slots = message.slots
local returnChests = message.returnChests
local output = message.output or "unknown"
if ok then
-- Show what was crafted
local results = {}
for slot = 1, 16 do
local detail = turtle.getItemDetail(slot)
if detail then
table.insert(results, string.format("%s x%d", detail.name, detail.count))
end
end
print("[CRAFT] Success! Result: " .. table.concat(results, ", "))
else
print("[CRAFT] Failed: " .. tostring(err))
end
if not slots or not returnChests then
print("[CRAFT] Invalid command: missing slots or returnChests")
return { type = "craft_result", success = false, error = "Invalid command" }
end
-- Wait for master to pull results before polling again
sleep(2)
print(string.format("[CRAFT] Received craft request: %s", output))
-- 1. Clear turtle inventory (safety — should be empty)
clearInventory(returnChests)
-- 2. Pull ingredients from chests into crafting grid slots
local placedItems = {} -- turtleSlot -> itemName
local allPlaced = true
for turtleSlotStr, info in pairs(slots) do
local turtleSlot = tonumber(turtleSlotStr)
local chestName = info.chestName
local chestSlot = info.chestSlot
local itemName = info.itemName
local count = info.count or 1
print(string.format("[CRAFT] Pulling %s from %s slot %d -> turtle slot %d",
itemName, chestName, chestSlot, turtleSlot))
local ok, n = pcall(selfInv.pullItems, chestName, chestSlot, count, turtleSlot)
if ok and n and n > 0 then
placedItems[turtleSlot] = itemName
print(string.format("[CRAFT] Placed %s x%d in slot %d", itemName, n, turtleSlot))
else
print("[CRAFT] Items removed before crafting (aborted by master)")
if not ok then
print(string.format("[CRAFT] pullItems error: %s", tostring(n)))
else
print(string.format("[CRAFT] pullItems returned %s (expected %d)", tostring(n), count))
end
allPlaced = false
break
end
end
sleep(POLL_INTERVAL)
if not allPlaced then
-- Abort: push everything back
print("[CRAFT] Failed to place all ingredients, aborting")
local returnedItems = {}
for slot, itemName in pairs(placedItems) do
returnedItems[tostring(slot)] = itemName
end
clearInventory(returnChests)
return {
type = "craft_result",
success = false,
error = "Failed to pull ingredients",
returnedItems = returnedItems,
}
end
-- 3. Craft
print("[CRAFT] Grid contents:")
print(describeGrid())
print("[CRAFT] Attempting craft...")
local craftOk, craftErr = turtle.craft()
if not craftOk then
print("[CRAFT] Failed: " .. tostring(craftErr))
-- Collect what's still in the grid for the master to track
local returnedItems = {}
for slot, itemName in pairs(placedItems) do
returnedItems[tostring(slot)] = itemName
end
-- Push ingredients back
clearInventory(returnChests)
return {
type = "craft_result",
success = false,
error = "Craft failed: " .. tostring(craftErr),
returnedItems = returnedItems,
}
end
-- 4. Collect results info
local results = {}
local totalOutput = 0
for slot = 1, 16 do
local detail = turtle.getItemDetail(slot)
if detail then
table.insert(results, { name = detail.name, count = detail.count, slot = slot })
totalOutput = totalOutput + detail.count
end
end
print("[CRAFT] Success! Output:")
for _, r in ipairs(results) do
print(string.format(" %s x%d", r.name, r.count))
end
-- 5. Push results back to chests
local pushed = clearInventory(returnChests)
print(string.format("[CRAFT] Pushed %d items back to storage", pushed))
return {
type = "craft_result",
success = true,
output = output,
totalOutput = totalOutput,
results = results,
}
end
-------------------------------------------------
-- Main loop: listen for modem commands
-------------------------------------------------
while true do
local event, side, channel, replyChannel, message, distance = os.pullEvent("modem_message")
if channel == CRAFT_CHANNEL and type(message) == "table" then
if message.type == "craft_request" then
local result = handleCraftCommand(message)
-- Send result back to master
modem.transmit(CRAFT_REPLY_CHANNEL, CRAFT_CHANNEL, result)
elseif message.type == "ping" then
-- Health check from master
modem.transmit(CRAFT_REPLY_CHANNEL, CRAFT_CHANNEL, {
type = "pong",
name = selfName,
})
end
end
end