Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- startup
- -- Swarm Miner Worker v14 BROADCAST-ASSIGN
- -- Slot 1 = Worker Fuel, Slot 2-16 = Loot
- -- No scan dependency. A fresh/unassigned worker accepts assign_open broadcasts.
- local VERSION = 14
- local PROTOCOL = "swarm_miner_v14"
- local SAVE_FILE = "/swarm_worker_v14.state"
- local FUEL_SLOT = 1
- local LOOT_FIRST = 2
- local LOOT_LAST = 16
- local FUEL_TARGET = 300
- local RETURN_BUFFER = 100
- local id = os.getComputerID()
- local modemSide = nil
- local modemOpen = false
- local lastHello = 0
- local S = {
- network = "swarm",
- master = nil,
- assigned = false,
- index = nil,
- total = 8,
- home = nil,
- chest = nil,
- facing = nil,
- right = nil,
- homeRightOffset = 0,
- pos = {f=0,r=0,d=0,dir=0},
- state = "idle",
- status = "boot",
- job = nil,
- sector = nil,
- needSector = false,
- stats = {blocks=0,sectors=0,unloads=0},
- }
- local JUNK = {
- ["minecraft:cobblestone"]=true,
- ["minecraft:cobbled_deepslate"]=true,
- ["minecraft:deepslate"]=true,
- ["minecraft:dirt"]=true,
- ["minecraft:gravel"]=true,
- ["minecraft:tuff"]=true,
- }
- local function now()
- if os.epoch then return math.floor(os.epoch("utc")/1000) end
- return math.floor(os.clock())
- end
- local function clear()
- term.clear()
- term.setCursorPos(1,1)
- end
- local function clone(t)
- if type(t)~="table" then return t end
- local o={}
- for k,v in pairs(t) do o[k]=clone(v) end
- return o
- end
- local function save()
- local f=fs.open(SAVE_FILE,"w")
- if f then f.write(textutils.serialize(S)); f.close() end
- end
- local function load()
- if not fs.exists(SAVE_FILE) then return false end
- local f=fs.open(SAVE_FILE,"r")
- if not f then return false end
- local raw=f.readAll(); f.close()
- local ok,data=pcall(textutils.unserialize, raw)
- if ok and type(data)=="table" then
- S=data
- S.pos=S.pos or {f=0,r=0,d=0,dir=0}
- S.stats=S.stats or {blocks=0,sectors=0,unloads=0}
- return true
- end
- return false
- end
- local function sideHas(side, typ)
- if not peripheral.isPresent(side) then return false end
- if peripheral.hasType then
- local ok,res=pcall(peripheral.hasType, side, typ)
- if ok and res then return true end
- end
- local t=peripheral.getType(side)
- if type(t)=="string" then return t==typ end
- if type(t)=="table" then for _,v in ipairs(t) do if v==typ then return true end end end
- return false
- end
- local function openModem()
- for _,s in ipairs({"left","right","top","bottom","front","back"}) do
- if sideHas(s,"modem") then
- local ok=pcall(rednet.open,s)
- if ok then modemSide=s; modemOpen=true; return true end
- end
- end
- return false
- end
- local function env(tp, body)
- body=body or {}
- body.protocol=PROTOCOL
- body.version=VERSION
- body.network=S.network
- body.type=tp
- body.from=id
- body.time=now()
- return body
- end
- local function valid(msg)
- return type(msg)=="table" and msg.protocol==PROTOCOL and msg.version==VERSION and msg.network==S.network
- end
- local function send(tp, body)
- if S.master then rednet.send(S.master, env(tp, body or {}), PROTOCOL)
- else rednet.broadcast(env(tp, body or {}), PROTOCOL) end
- end
- local function worldPos()
- if not S.home or not S.facing or not S.right then return {x=0,y=0,z=0} end
- return {
- x=S.home.x + S.facing.dx*S.pos.f + S.right.dx*S.pos.r,
- y=S.home.y - S.pos.d,
- z=S.home.z + S.facing.dz*S.pos.f + S.right.dz*S.pos.r,
- }
- end
- local function draw()
- clear()
- print("=== Swarm Worker v14 ===")
- print("ID: "..id.." Modem: "..tostring(modemSide))
- print("State: "..tostring(S.state))
- print("Status: "..tostring(S.status))
- print("Assigned: "..tostring(S.assigned).." #"..tostring(S.index or "?").."/"..tostring(S.total or "?"))
- print("Master: "..tostring(S.master))
- print("Job: "..tostring(S.job and S.job.id or "none"))
- print("Sector: "..tostring(S.sector and S.sector.id or "none"))
- print("Fuel: "..tostring(turtle.getFuelLevel()))
- print("Pos f/r/d/dir: "..S.pos.f.."/"..S.pos.r.."/"..S.pos.d.."/"..S.pos.dir)
- local w=worldPos()
- print("World: "..w.x.." "..w.y.." "..w.z)
- print("Blocks: "..S.stats.blocks.." Sectors: "..S.stats.sectors.." Unloads: "..S.stats.unloads)
- end
- local function status()
- send("status", {
- state=S.state,status=S.status,assigned=S.assigned,index=S.index,
- fuel=turtle.getFuelLevel(),pos=clone(S.pos),world=worldPos(),
- jobId=S.job and S.job.id or nil,sector=S.sector,stats=clone(S.stats),
- })
- end
- local function refuel(target)
- if turtle.getFuelLevel()=="unlimited" then return true end
- target=target or FUEL_TARGET
- while turtle.getFuelLevel()<target do
- turtle.select(FUEL_SLOT)
- if turtle.getItemCount(FUEL_SLOT)<=0 then
- S.state="fuel_wait"; S.status="Fuel in Slot 1 fehlt"
- draw(); status()
- sleep(1)
- return false
- end
- if not turtle.refuel(1) then
- S.state="fuel_wait"; S.status="Slot 1 ist kein Fuel"
- draw(); status()
- sleep(1)
- return false
- end
- end
- return true
- end
- local function chooseLootSlot()
- for i=LOOT_FIRST,LOOT_LAST do
- if turtle.getItemCount(i)==0 then turtle.select(i); return true end
- end
- for i=LOOT_FIRST,LOOT_LAST do
- if turtle.getItemSpace(i)>0 then turtle.select(i); return true end
- end
- turtle.select(LOOT_FIRST)
- return false
- end
- local function inventoryFull()
- for i=LOOT_FIRST,LOOT_LAST do if turtle.getItemCount(i)==0 then return false end end
- return true
- end
- local function dropJunk()
- if not S.job or S.job.junkMode=="off" then return end
- if S.job.junkMode=="full" and not inventoryFull() then return end
- for i=LOOT_FIRST,LOOT_LAST do
- local d=turtle.getItemDetail(i)
- if d and JUNK[d.name] then turtle.select(i); turtle.dropUp() end
- end
- end
- local function protected(p)
- if not S.job or not S.job.protectedZones then return false end
- for _,z in ipairs(S.job.protectedZones) do
- if p.f>=z.f1 and p.f<=z.f2 and p.r>=z.r1 and p.r<=z.r2 and p.d>=z.d1 and p.d<=z.d2 then
- return true,z.name
- end
- end
- return false,nil
- end
- local function frontTarget()
- local p=clone(S.pos)
- if p.dir==0 then p.f=p.f+1 elseif p.dir==1 then p.r=p.r+1 elseif p.dir==2 then p.f=p.f-1 else p.r=p.r-1 end
- return p
- end
- local function upTarget()
- local p=clone(S.pos); p.d=p.d-1; return p
- end
- local function downTarget()
- local p=clone(S.pos); p.d=p.d+1; return p
- end
- local function digForward()
- local bad,name=protected(frontTarget())
- if bad then S.state="halt"; S.status="Protected front "..tostring(name); save(); return false end
- chooseLootSlot()
- local fail=0
- while turtle.detect() do
- turtle.attack()
- if turtle.dig() then
- S.stats.blocks=S.stats.blocks+1; fail=0
- else
- fail=fail+1
- if fail>20 then S.state="halt"; S.status="Front block unbreakable"; save(); return false end
- end
- sleep(0.03)
- end
- return true
- end
- local function digUp()
- local bad,name=protected(upTarget())
- if bad then return false end
- chooseLootSlot()
- while turtle.detectUp() do turtle.attackUp(); if turtle.digUp() then S.stats.blocks=S.stats.blocks+1 end; sleep(0.03) end
- return true
- end
- local function digDown()
- local bad,name=protected(downTarget())
- if bad then return false end
- chooseLootSlot()
- while turtle.detectDown() do turtle.attackDown(); if turtle.digDown() then S.stats.blocks=S.stats.blocks+1 end; sleep(0.03) end
- return true
- end
- local function turnLeft()
- turtle.turnLeft()
- S.pos.dir=(S.pos.dir+3)%4
- end
- local function turnRight()
- turtle.turnRight()
- S.pos.dir=(S.pos.dir+1)%4
- end
- local function turnTo(d)
- d=d%4
- local diff=(d-S.pos.dir)%4
- if diff==1 then turnRight()
- elseif diff==3 then turnLeft()
- elseif diff==2 then turnRight(); turnRight() end
- save()
- end
- local function forward()
- if not refuel(10) then return false end
- if not digForward() then return false end
- local target=frontTarget()
- local stuck=0
- while not turtle.forward() do
- turtle.attack()
- digForward()
- stuck=stuck+1
- if stuck>80 then S.state="halt"; S.status="Stuck forward"; save(); return false end
- sleep(0.05)
- end
- S.pos=target
- save()
- return true
- end
- local function up()
- if not refuel(10) then return false end
- if not digUp() then return false end
- local target=upTarget()
- local stuck=0
- while not turtle.up() do turtle.attackUp(); digUp(); stuck=stuck+1; if stuck>80 then S.state="halt"; S.status="Stuck up"; save(); return false end; sleep(0.05) end
- S.pos=target; save(); return true
- end
- local function down()
- if not refuel(10) then return false end
- if not digDown() then return false end
- local target=downTarget()
- local stuck=0
- while not turtle.down() do turtle.attackDown(); digDown(); stuck=stuck+1; if stuck>80 then S.state="halt"; S.status="Stuck down"; save(); return false end; sleep(0.05) end
- S.pos=target; save(); return true
- end
- local function moveD(d)
- while S.pos.d<d do if not down() then return false end end
- while S.pos.d>d do if not up() then return false end end
- return true
- end
- local function moveR(r)
- while S.pos.r<r do turnTo(1); if not forward() then return false end end
- while S.pos.r>r do turnTo(3); if not forward() then return false end end
- return true
- end
- local function moveF(f)
- while S.pos.f<f do turnTo(0); if not forward() then return false end end
- while S.pos.f>f do turnTo(2); if not forward() then return false end end
- return true
- end
- local function travelTo(f,r,d)
- if not moveD(d) then return false end
- if not moveR(r) then return false end
- if not moveF(f) then return false end
- return true
- end
- local function unload(returnAfter)
- local rp=clone(S.pos)
- S.state="unloading"; S.status="Zum Dock"
- draw(); status()
- travelTo(0,0,0)
- turnTo(2)
- for i=LOOT_FIRST,LOOT_LAST do
- turtle.select(i)
- while turtle.getItemCount(i)>0 do
- if not turtle.drop() then
- S.status="Kiste voll"
- draw(); status()
- sleep(2)
- end
- end
- end
- S.stats.unloads=S.stats.unloads+1
- turnTo(0)
- if returnAfter and S.job and S.sector then
- S.state="mining"; S.status="Zurück"
- travelTo(rp.f,rp.r,rp.d); turnTo(rp.dir)
- else
- S.state="idle"; S.status="Am Dock"
- end
- save(); status()
- end
- local function pollOnce(timeout)
- local sender,msg=rednet.receive(PROTOCOL, timeout or 0)
- if sender and valid(msg) then
- if msg.type=="ping" then
- status()
- elseif (msg.type=="assign_open" and not S.assigned) or (msg.type=="assign" and (not msg.to or msg.to==id)) then
- S.master=sender
- S.assigned=true
- S.index=msg.index
- S.total=msg.total or 8
- S.home=msg.home
- S.chest=msg.chest
- S.facing=msg.facing
- S.right=msg.right
- S.homeRightOffset=msg.homeRightOffset or 0
- S.pos={f=0,r=0,d=0,dir=0}
- S.state="idle"; S.status="Assigned W"..tostring(S.index)
- save()
- send("assign_ack",{index=S.index,state=S.state,status=S.status,fuel=turtle.getFuelLevel()})
- elseif (msg.type=="job_broadcast" or msg.type=="job_start") and msg.job then
- if msg.type=="job_start" and msg.to and msg.to~=id then return end
- if S.assigned then
- S.master=sender
- S.job=msg.job
- S.sector=nil
- S.needSector=true
- S.state="mining"
- S.status="Job empfangen"
- save()
- status()
- end
- elseif msg.type=="sector_reply" and (not msg.to or msg.to==id) then
- S.sector=msg.sector
- if S.sector then
- S.state="mining"; S.status="Sector "..S.sector.id
- else
- S.state="done"; S.status="Keine Sektoren"
- unload(false)
- end
- save()
- elseif msg.type=="return" then
- unload(false)
- elseif msg.type=="reset" then
- fs.delete(SAVE_FILE)
- os.reboot()
- end
- end
- end
- local function pollAll()
- while true do
- local sender,msg=rednet.receive(PROTOCOL,0)
- if not sender then break end
- if valid(msg) then
- if msg.type=="ping" then status()
- elseif (msg.type=="assign_open" and not S.assigned) or (msg.type=="assign" and (not msg.to or msg.to==id)) then
- S.master=sender; S.assigned=true; S.index=msg.index; S.total=msg.total or 8; S.home=msg.home; S.chest=msg.chest; S.facing=msg.facing; S.right=msg.right; S.homeRightOffset=msg.homeRightOffset or 0; S.pos={f=0,r=0,d=0,dir=0}; S.state="idle"; S.status="Assigned W"..tostring(S.index); save(); send("assign_ack",{index=S.index,state=S.state,status=S.status,fuel=turtle.getFuelLevel()})
- elseif (msg.type=="job_broadcast" or msg.type=="job_start") and msg.job then
- if msg.type=="job_start" and msg.to and msg.to~=id then
- elseif S.assigned then S.master=sender; S.job=msg.job; S.sector=nil; S.needSector=true; S.state="mining"; S.status="Job empfangen"; save(); status() end
- elseif msg.type=="sector_reply" and (not msg.to or msg.to==id) then
- S.sector=msg.sector; if S.sector then S.state="mining"; S.status="Sector "..S.sector.id else S.state="done"; S.status="Keine Sektoren"; unload(false) end; save()
- elseif msg.type=="return" then unload(false)
- elseif msg.type=="reset" then fs.delete(SAVE_FILE); os.reboot() end
- end
- end
- end
- local function requestSector()
- if not S.master then return false end
- S.sector=nil
- S.status="Fordere Sector"
- for i=1,20 do
- send("sector_request",{index=S.index,world=worldPos()})
- local endAt=os.clock()+0.25
- while os.clock()<endAt do
- pollOnce(0.05)
- if S.sector or S.state=="done" then return S.sector ~= nil end
- end
- end
- S.status="Kein Sector Reply"
- return false
- end
- local function target(row, step, length, rStart)
- if row%2==0 then return step, rStart+row end
- return length-1-step, rStart+row
- end
- local function mineCell(workD, depth)
- -- 3-high mining: turtle at middle level if possible.
- if workD>0 then digUp() end
- if workD+1<depth then digDown() end
- dropJunk()
- if inventoryFull() then unload(true) end
- if turtle.getFuelLevel()~="unlimited" and turtle.getFuelLevel()<math.abs(S.pos.f)+math.abs(S.pos.r)+math.abs(S.pos.d)+RETURN_BUFFER then unload(true) end
- end
- local function mineSector(sec)
- S.sector=sec
- local length=tonumber(S.job.length) or 1
- local depth=tonumber(S.job.depth) or 1
- local width=tonumber(sec.width) or 1
- local localRStart=(sec.r1 or 0)-(S.homeRightOffset or 0)
- local startBlocks=S.stats.blocks
- local layer=0
- while layer<depth do
- local workD=layer
- if layer+1<depth then workD=layer+1 end
- for row=0,width-1 do
- for step=0,length-1 do
- pollAll()
- if S.state=="halt" then return false end
- local f,r=target(row,step,length,localRStart)
- S.state="mining"; S.status="S"..sec.id.." L"..workD.." R"..(row+1).."/"..width.." C"..(step+1).."/"..length
- draw()
- if not travelTo(f,r,workD) then return false end
- turnTo(0)
- mineCell(workD,depth)
- if now()-lastHello>=2 then status(); lastHello=now() end
- end
- end
- travelTo(0,0,workD); turnTo(0)
- layer=layer+3
- end
- S.stats.sectors=S.stats.sectors+1
- local blocks=S.stats.blocks-startBlocks
- send("sector_done",{sectorId=sec.id,blocks=blocks,index=S.index})
- unload(false)
- S.sector=nil
- save(); status()
- return true
- end
- local function workStep()
- if not S.assigned then S.state="idle"; S.status="Warte auf Assign"; return end
- if not S.job then S.state="idle"; S.status="Warte auf GO"; return end
- if not refuel(FUEL_TARGET) then return end
- if not S.sector then
- if not requestSector() then return end
- end
- if S.sector then
- mineSector(S.sector)
- end
- end
- local function main()
- os.setComputerLabel("SwarmWorker-v14-"..id)
- load()
- if not openModem() then clear(); print("Kein Wireless/Ender-Modem gefunden."); return end
- draw()
- send("hello",{state=S.state,status=S.status,assigned=S.assigned,index=S.index,fuel=turtle.getFuelLevel()})
- lastHello=now()
- while true do
- pollAll()
- if S.assigned and S.job and S.state~="halt" then
- workStep()
- else
- if now()-lastHello>=1 then
- send("hello",{state=S.state,status=S.status,assigned=S.assigned,index=S.index,fuel=turtle.getFuelLevel()})
- status()
- lastHello=now()
- draw()
- end
- pollOnce(0.2)
- end
- end
- end
- main()
Advertisement
Add Comment
Please, Sign In to add comment