babulm

Untitled

Mar 23rd, 2020 (edited)
123
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 16.25 KB | None | 0 0
  1. -- startup
  2. -- Swarm Miner Worker v14 BROADCAST-ASSIGN
  3. -- Slot 1 = Worker Fuel, Slot 2-16 = Loot
  4. -- No scan dependency. A fresh/unassigned worker accepts assign_open broadcasts.
  5.  
  6. local VERSION = 14
  7. local PROTOCOL = "swarm_miner_v14"
  8. local SAVE_FILE = "/swarm_worker_v14.state"
  9.  
  10. local FUEL_SLOT = 1
  11. local LOOT_FIRST = 2
  12. local LOOT_LAST = 16
  13. local FUEL_TARGET = 300
  14. local RETURN_BUFFER = 100
  15.  
  16. local id = os.getComputerID()
  17. local modemSide = nil
  18. local modemOpen = false
  19. local lastHello = 0
  20.  
  21. local S = {
  22. network = "swarm",
  23. master = nil,
  24. assigned = false,
  25. index = nil,
  26. total = 8,
  27.  
  28. home = nil,
  29. chest = nil,
  30. facing = nil,
  31. right = nil,
  32. homeRightOffset = 0,
  33.  
  34. pos = {f=0,r=0,d=0,dir=0},
  35.  
  36. state = "idle",
  37. status = "boot",
  38. job = nil,
  39. sector = nil,
  40. needSector = false,
  41.  
  42. stats = {blocks=0,sectors=0,unloads=0},
  43. }
  44.  
  45. local JUNK = {
  46. ["minecraft:cobblestone"]=true,
  47. ["minecraft:cobbled_deepslate"]=true,
  48. ["minecraft:deepslate"]=true,
  49. ["minecraft:dirt"]=true,
  50. ["minecraft:gravel"]=true,
  51. ["minecraft:tuff"]=true,
  52. }
  53.  
  54. local function now()
  55. if os.epoch then return math.floor(os.epoch("utc")/1000) end
  56. return math.floor(os.clock())
  57. end
  58.  
  59. local function clear()
  60. term.clear()
  61. term.setCursorPos(1,1)
  62. end
  63.  
  64. local function clone(t)
  65. if type(t)~="table" then return t end
  66. local o={}
  67. for k,v in pairs(t) do o[k]=clone(v) end
  68. return o
  69. end
  70.  
  71. local function save()
  72. local f=fs.open(SAVE_FILE,"w")
  73. if f then f.write(textutils.serialize(S)); f.close() end
  74. end
  75.  
  76. local function load()
  77. if not fs.exists(SAVE_FILE) then return false end
  78. local f=fs.open(SAVE_FILE,"r")
  79. if not f then return false end
  80. local raw=f.readAll(); f.close()
  81. local ok,data=pcall(textutils.unserialize, raw)
  82. if ok and type(data)=="table" then
  83. S=data
  84. S.pos=S.pos or {f=0,r=0,d=0,dir=0}
  85. S.stats=S.stats or {blocks=0,sectors=0,unloads=0}
  86. return true
  87. end
  88. return false
  89. end
  90.  
  91. local function sideHas(side, typ)
  92. if not peripheral.isPresent(side) then return false end
  93. if peripheral.hasType then
  94. local ok,res=pcall(peripheral.hasType, side, typ)
  95. if ok and res then return true end
  96. end
  97. local t=peripheral.getType(side)
  98. if type(t)=="string" then return t==typ end
  99. if type(t)=="table" then for _,v in ipairs(t) do if v==typ then return true end end end
  100. return false
  101. end
  102.  
  103. local function openModem()
  104. for _,s in ipairs({"left","right","top","bottom","front","back"}) do
  105. if sideHas(s,"modem") then
  106. local ok=pcall(rednet.open,s)
  107. if ok then modemSide=s; modemOpen=true; return true end
  108. end
  109. end
  110. return false
  111. end
  112.  
  113. local function env(tp, body)
  114. body=body or {}
  115. body.protocol=PROTOCOL
  116. body.version=VERSION
  117. body.network=S.network
  118. body.type=tp
  119. body.from=id
  120. body.time=now()
  121. return body
  122. end
  123.  
  124. local function valid(msg)
  125. return type(msg)=="table" and msg.protocol==PROTOCOL and msg.version==VERSION and msg.network==S.network
  126. end
  127.  
  128. local function send(tp, body)
  129. if S.master then rednet.send(S.master, env(tp, body or {}), PROTOCOL)
  130. else rednet.broadcast(env(tp, body or {}), PROTOCOL) end
  131. end
  132.  
  133. local function worldPos()
  134. if not S.home or not S.facing or not S.right then return {x=0,y=0,z=0} end
  135. return {
  136. x=S.home.x + S.facing.dx*S.pos.f + S.right.dx*S.pos.r,
  137. y=S.home.y - S.pos.d,
  138. z=S.home.z + S.facing.dz*S.pos.f + S.right.dz*S.pos.r,
  139. }
  140. end
  141.  
  142. local function draw()
  143. clear()
  144. print("=== Swarm Worker v14 ===")
  145. print("ID: "..id.." Modem: "..tostring(modemSide))
  146. print("State: "..tostring(S.state))
  147. print("Status: "..tostring(S.status))
  148. print("Assigned: "..tostring(S.assigned).." #"..tostring(S.index or "?").."/"..tostring(S.total or "?"))
  149. print("Master: "..tostring(S.master))
  150. print("Job: "..tostring(S.job and S.job.id or "none"))
  151. print("Sector: "..tostring(S.sector and S.sector.id or "none"))
  152. print("Fuel: "..tostring(turtle.getFuelLevel()))
  153. print("Pos f/r/d/dir: "..S.pos.f.."/"..S.pos.r.."/"..S.pos.d.."/"..S.pos.dir)
  154. local w=worldPos()
  155. print("World: "..w.x.." "..w.y.." "..w.z)
  156. print("Blocks: "..S.stats.blocks.." Sectors: "..S.stats.sectors.." Unloads: "..S.stats.unloads)
  157. end
  158.  
  159. local function status()
  160. send("status", {
  161. state=S.state,status=S.status,assigned=S.assigned,index=S.index,
  162. fuel=turtle.getFuelLevel(),pos=clone(S.pos),world=worldPos(),
  163. jobId=S.job and S.job.id or nil,sector=S.sector,stats=clone(S.stats),
  164. })
  165. end
  166.  
  167. local function refuel(target)
  168. if turtle.getFuelLevel()=="unlimited" then return true end
  169. target=target or FUEL_TARGET
  170. while turtle.getFuelLevel()<target do
  171. turtle.select(FUEL_SLOT)
  172. if turtle.getItemCount(FUEL_SLOT)<=0 then
  173. S.state="fuel_wait"; S.status="Fuel in Slot 1 fehlt"
  174. draw(); status()
  175. sleep(1)
  176. return false
  177. end
  178. if not turtle.refuel(1) then
  179. S.state="fuel_wait"; S.status="Slot 1 ist kein Fuel"
  180. draw(); status()
  181. sleep(1)
  182. return false
  183. end
  184. end
  185. return true
  186. end
  187.  
  188. local function chooseLootSlot()
  189. for i=LOOT_FIRST,LOOT_LAST do
  190. if turtle.getItemCount(i)==0 then turtle.select(i); return true end
  191. end
  192. for i=LOOT_FIRST,LOOT_LAST do
  193. if turtle.getItemSpace(i)>0 then turtle.select(i); return true end
  194. end
  195. turtle.select(LOOT_FIRST)
  196. return false
  197. end
  198.  
  199. local function inventoryFull()
  200. for i=LOOT_FIRST,LOOT_LAST do if turtle.getItemCount(i)==0 then return false end end
  201. return true
  202. end
  203.  
  204. local function dropJunk()
  205. if not S.job or S.job.junkMode=="off" then return end
  206. if S.job.junkMode=="full" and not inventoryFull() then return end
  207. for i=LOOT_FIRST,LOOT_LAST do
  208. local d=turtle.getItemDetail(i)
  209. if d and JUNK[d.name] then turtle.select(i); turtle.dropUp() end
  210. end
  211. end
  212.  
  213. local function protected(p)
  214. if not S.job or not S.job.protectedZones then return false end
  215. for _,z in ipairs(S.job.protectedZones) do
  216. 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
  217. return true,z.name
  218. end
  219. end
  220. return false,nil
  221. end
  222.  
  223. local function frontTarget()
  224. local p=clone(S.pos)
  225. 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
  226. return p
  227. end
  228.  
  229. local function upTarget()
  230. local p=clone(S.pos); p.d=p.d-1; return p
  231. end
  232.  
  233. local function downTarget()
  234. local p=clone(S.pos); p.d=p.d+1; return p
  235. end
  236.  
  237. local function digForward()
  238. local bad,name=protected(frontTarget())
  239. if bad then S.state="halt"; S.status="Protected front "..tostring(name); save(); return false end
  240. chooseLootSlot()
  241. local fail=0
  242. while turtle.detect() do
  243. turtle.attack()
  244. if turtle.dig() then
  245. S.stats.blocks=S.stats.blocks+1; fail=0
  246. else
  247. fail=fail+1
  248. if fail>20 then S.state="halt"; S.status="Front block unbreakable"; save(); return false end
  249. end
  250. sleep(0.03)
  251. end
  252. return true
  253. end
  254.  
  255. local function digUp()
  256. local bad,name=protected(upTarget())
  257. if bad then return false end
  258. chooseLootSlot()
  259. while turtle.detectUp() do turtle.attackUp(); if turtle.digUp() then S.stats.blocks=S.stats.blocks+1 end; sleep(0.03) end
  260. return true
  261. end
  262.  
  263. local function digDown()
  264. local bad,name=protected(downTarget())
  265. if bad then return false end
  266. chooseLootSlot()
  267. while turtle.detectDown() do turtle.attackDown(); if turtle.digDown() then S.stats.blocks=S.stats.blocks+1 end; sleep(0.03) end
  268. return true
  269. end
  270.  
  271. local function turnLeft()
  272. turtle.turnLeft()
  273. S.pos.dir=(S.pos.dir+3)%4
  274. end
  275.  
  276. local function turnRight()
  277. turtle.turnRight()
  278. S.pos.dir=(S.pos.dir+1)%4
  279. end
  280.  
  281. local function turnTo(d)
  282. d=d%4
  283. local diff=(d-S.pos.dir)%4
  284. if diff==1 then turnRight()
  285. elseif diff==3 then turnLeft()
  286. elseif diff==2 then turnRight(); turnRight() end
  287. save()
  288. end
  289.  
  290. local function forward()
  291. if not refuel(10) then return false end
  292. if not digForward() then return false end
  293. local target=frontTarget()
  294. local stuck=0
  295. while not turtle.forward() do
  296. turtle.attack()
  297. digForward()
  298. stuck=stuck+1
  299. if stuck>80 then S.state="halt"; S.status="Stuck forward"; save(); return false end
  300. sleep(0.05)
  301. end
  302. S.pos=target
  303. save()
  304. return true
  305. end
  306.  
  307. local function up()
  308. if not refuel(10) then return false end
  309. if not digUp() then return false end
  310. local target=upTarget()
  311. local stuck=0
  312. 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
  313. S.pos=target; save(); return true
  314. end
  315.  
  316. local function down()
  317. if not refuel(10) then return false end
  318. if not digDown() then return false end
  319. local target=downTarget()
  320. local stuck=0
  321. 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
  322. S.pos=target; save(); return true
  323. end
  324.  
  325. local function moveD(d)
  326. while S.pos.d<d do if not down() then return false end end
  327. while S.pos.d>d do if not up() then return false end end
  328. return true
  329. end
  330.  
  331. local function moveR(r)
  332. while S.pos.r<r do turnTo(1); if not forward() then return false end end
  333. while S.pos.r>r do turnTo(3); if not forward() then return false end end
  334. return true
  335. end
  336.  
  337. local function moveF(f)
  338. while S.pos.f<f do turnTo(0); if not forward() then return false end end
  339. while S.pos.f>f do turnTo(2); if not forward() then return false end end
  340. return true
  341. end
  342.  
  343. local function travelTo(f,r,d)
  344. if not moveD(d) then return false end
  345. if not moveR(r) then return false end
  346. if not moveF(f) then return false end
  347. return true
  348. end
  349.  
  350. local function unload(returnAfter)
  351. local rp=clone(S.pos)
  352. S.state="unloading"; S.status="Zum Dock"
  353. draw(); status()
  354. travelTo(0,0,0)
  355. turnTo(2)
  356. for i=LOOT_FIRST,LOOT_LAST do
  357. turtle.select(i)
  358. while turtle.getItemCount(i)>0 do
  359. if not turtle.drop() then
  360. S.status="Kiste voll"
  361. draw(); status()
  362. sleep(2)
  363. end
  364. end
  365. end
  366. S.stats.unloads=S.stats.unloads+1
  367. turnTo(0)
  368. if returnAfter and S.job and S.sector then
  369. S.state="mining"; S.status="Zurück"
  370. travelTo(rp.f,rp.r,rp.d); turnTo(rp.dir)
  371. else
  372. S.state="idle"; S.status="Am Dock"
  373. end
  374. save(); status()
  375. end
  376.  
  377. local function pollOnce(timeout)
  378. local sender,msg=rednet.receive(PROTOCOL, timeout or 0)
  379. if sender and valid(msg) then
  380. if msg.type=="ping" then
  381. status()
  382. elseif (msg.type=="assign_open" and not S.assigned) or (msg.type=="assign" and (not msg.to or msg.to==id)) then
  383. S.master=sender
  384. S.assigned=true
  385. S.index=msg.index
  386. S.total=msg.total or 8
  387. S.home=msg.home
  388. S.chest=msg.chest
  389. S.facing=msg.facing
  390. S.right=msg.right
  391. S.homeRightOffset=msg.homeRightOffset or 0
  392. S.pos={f=0,r=0,d=0,dir=0}
  393. S.state="idle"; S.status="Assigned W"..tostring(S.index)
  394. save()
  395. send("assign_ack",{index=S.index,state=S.state,status=S.status,fuel=turtle.getFuelLevel()})
  396. elseif (msg.type=="job_broadcast" or msg.type=="job_start") and msg.job then
  397. if msg.type=="job_start" and msg.to and msg.to~=id then return end
  398. if S.assigned then
  399. S.master=sender
  400. S.job=msg.job
  401. S.sector=nil
  402. S.needSector=true
  403. S.state="mining"
  404. S.status="Job empfangen"
  405. save()
  406. status()
  407. end
  408. elseif msg.type=="sector_reply" and (not msg.to or msg.to==id) then
  409. S.sector=msg.sector
  410. if S.sector then
  411. S.state="mining"; S.status="Sector "..S.sector.id
  412. else
  413. S.state="done"; S.status="Keine Sektoren"
  414. unload(false)
  415. end
  416. save()
  417. elseif msg.type=="return" then
  418. unload(false)
  419. elseif msg.type=="reset" then
  420. fs.delete(SAVE_FILE)
  421. os.reboot()
  422. end
  423. end
  424. end
  425.  
  426. local function pollAll()
  427. while true do
  428. local sender,msg=rednet.receive(PROTOCOL,0)
  429. if not sender then break end
  430. if valid(msg) then
  431. if msg.type=="ping" then status()
  432. elseif (msg.type=="assign_open" and not S.assigned) or (msg.type=="assign" and (not msg.to or msg.to==id)) then
  433. 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()})
  434. elseif (msg.type=="job_broadcast" or msg.type=="job_start") and msg.job then
  435. if msg.type=="job_start" and msg.to and msg.to~=id then
  436. 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
  437. elseif msg.type=="sector_reply" and (not msg.to or msg.to==id) then
  438. 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()
  439. elseif msg.type=="return" then unload(false)
  440. elseif msg.type=="reset" then fs.delete(SAVE_FILE); os.reboot() end
  441. end
  442. end
  443. end
  444.  
  445. local function requestSector()
  446. if not S.master then return false end
  447. S.sector=nil
  448. S.status="Fordere Sector"
  449. for i=1,20 do
  450. send("sector_request",{index=S.index,world=worldPos()})
  451. local endAt=os.clock()+0.25
  452. while os.clock()<endAt do
  453. pollOnce(0.05)
  454. if S.sector or S.state=="done" then return S.sector ~= nil end
  455. end
  456. end
  457. S.status="Kein Sector Reply"
  458. return false
  459. end
  460.  
  461. local function target(row, step, length, rStart)
  462. if row%2==0 then return step, rStart+row end
  463. return length-1-step, rStart+row
  464. end
  465.  
  466. local function mineCell(workD, depth)
  467. -- 3-high mining: turtle at middle level if possible.
  468. if workD>0 then digUp() end
  469. if workD+1<depth then digDown() end
  470. dropJunk()
  471. if inventoryFull() then unload(true) end
  472. 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
  473. end
  474.  
  475. local function mineSector(sec)
  476. S.sector=sec
  477. local length=tonumber(S.job.length) or 1
  478. local depth=tonumber(S.job.depth) or 1
  479. local width=tonumber(sec.width) or 1
  480. local localRStart=(sec.r1 or 0)-(S.homeRightOffset or 0)
  481. local startBlocks=S.stats.blocks
  482.  
  483. local layer=0
  484. while layer<depth do
  485. local workD=layer
  486. if layer+1<depth then workD=layer+1 end
  487. for row=0,width-1 do
  488. for step=0,length-1 do
  489. pollAll()
  490. if S.state=="halt" then return false end
  491. local f,r=target(row,step,length,localRStart)
  492. S.state="mining"; S.status="S"..sec.id.." L"..workD.." R"..(row+1).."/"..width.." C"..(step+1).."/"..length
  493. draw()
  494. if not travelTo(f,r,workD) then return false end
  495. turnTo(0)
  496. mineCell(workD,depth)
  497. if now()-lastHello>=2 then status(); lastHello=now() end
  498. end
  499. end
  500. travelTo(0,0,workD); turnTo(0)
  501. layer=layer+3
  502. end
  503.  
  504. S.stats.sectors=S.stats.sectors+1
  505. local blocks=S.stats.blocks-startBlocks
  506. send("sector_done",{sectorId=sec.id,blocks=blocks,index=S.index})
  507. unload(false)
  508. S.sector=nil
  509. save(); status()
  510. return true
  511. end
  512.  
  513. local function workStep()
  514. if not S.assigned then S.state="idle"; S.status="Warte auf Assign"; return end
  515. if not S.job then S.state="idle"; S.status="Warte auf GO"; return end
  516. if not refuel(FUEL_TARGET) then return end
  517. if not S.sector then
  518. if not requestSector() then return end
  519. end
  520. if S.sector then
  521. mineSector(S.sector)
  522. end
  523. end
  524.  
  525. local function main()
  526. os.setComputerLabel("SwarmWorker-v14-"..id)
  527. load()
  528. if not openModem() then clear(); print("Kein Wireless/Ender-Modem gefunden."); return end
  529. draw()
  530. send("hello",{state=S.state,status=S.status,assigned=S.assigned,index=S.index,fuel=turtle.getFuelLevel()})
  531. lastHello=now()
  532.  
  533. while true do
  534. pollAll()
  535. if S.assigned and S.job and S.state~="halt" then
  536. workStep()
  537. else
  538. if now()-lastHello>=1 then
  539. send("hello",{state=S.state,status=S.status,assigned=S.assigned,index=S.index,fuel=turtle.getFuelLevel()})
  540. status()
  541. lastHello=now()
  542. draw()
  543. end
  544. pollOnce(0.2)
  545. end
  546. end
  547. end
  548.  
  549. main()
  550.  
Advertisement
Add Comment
Please, Sign In to add comment