Add modem-based crafting functionality: implement command handling and inventory management
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user