· 5 years ago · Feb 23, 2021, 06:14 PM
1if not fs.exists('') then fs.makeDir('') end f=fs.open('/.heavdronic_data','w') f.write("{}") f.close() f=fs.open('/.packinfo','w') f.write("{\
2 testpkg1 = {\
3 version = \"0.1.1\",\
4 },\
5 spudnet = {\
6 version = \"0.1.0\",\
7 },\
8 binpath = {\
9 version = \"0.1.0\",\
10 },\
11 pkgm = {\
12 version = \"0.1.2-3\",\
13 },\
14 mfs = {\
15 version = \"0.1.5\",\
16 },\
17 testpkg2 = {\
18 version = \"0.1.0\",\
19 },\
20 multitask = {\
21 version = \"0.1.2-1\",\
22 },\
23}") f.close() if not fs.exists('/bin') then fs.makeDir('/bin') end f=fs.open('/bin/pkgm.lua','w') f.write("local args={...}\
24local pkg=dofile \"/lib/pkgm.lua\"\
25local flags={}\
26local ptr=#args\
27while ptr>0 do\
28 local c=args[ptr]\
29 if c==\"-i\" or c==\"--install-dir\" then\
30 if args[ptr+1] then\
31 flags.install_dir=args[ptr+1]\
32 table.remove(args,ptr+1)\
33 table.remove(args,ptr)\
34 else\
35 error(\"--install-dir needs an argument\")\
36 end\
37 end\
38 ptr=ptr-1\
39end\
40if flags.install_dir then\
41 local s,r=pkg.set_install_dir(flags.install_dir)\
42 if not s then error(r) end\
43end\
44if args[1]==\"i\" and args[2]~=nil then\
45 local deps=pkg.dependencies(args[2])\
46 local rstrt={false}\
47 if not deps then\
48 print(\"Already up to date, ?pioform.\")\
49 return\
50 end\
51 print(\"To be installed or updated:\\n\")\
52 print(table.concat(deps,\" \"))\
53 print(\"\\nPress the any key to continue.\")\
54 os.pullEvent(\"char\")\
55 pkg.pkginstmulti(deps,{[\"restart\"]=rstrt})\
56 print(\"Done!\")\
57elseif args[1]==\"rm\" and args[2]~=nil then\
58 table.remove(args,1)\
59 print(\"If this breaks things, you're completely responsible.\")\
60 print(\"Press the any key to continue.\")\
61 os.pullEvent(\"char\")\
62 for i,v in ipairs(args) do\
63 pkg.pkguinst(v)\
64 end\
65 print(\"Done.\")\
66else\
67 print(\"usage:\\ni <package>\\nupd <package>\\nrm <packages>\")\
68end") f.close() f=fs.open('/heavdrone.lua','w') f.write("local spudnet = dofile \"/lib/spudnet.lua\"\
69local mfs = dofile \"/lib/mfs.lua\"\
70\
71local hdata = mfs.load \".heavdronic_data\"\
72hdata.droneid = hdata.droneid or string.format(\"%06x\",math.random(0,0xFFFFFF))\
73mfs.save(\".heavdronic_data\",hdata)\
74\
75local p = spudnet.pool\
76local s\
77\
78local simple_buttons = {\
79 {\"shutdown\",\"Shut down\"},\
80 {\"reboot\",\"Reboot\"},\
81}\
82\
83local buttons={\
84 {\
85 [\"type\"]=\"textarea\",\
86 [\"respondon\"]=\"newname\",\
87 [\"name\"]=\"Label\"\
88 },{\
89 [\"type\"]=\"textarea\",\
90 [\"respondon\"]=\"newcategory\",\
91 [\"name\"]=\"Categorize\"\
92 },\
93}\
94\
95for i,v in ipairs(simple_buttons) do\
96 table.insert(buttons,{\
97 [\"type\"]=\"simple\",\
98 [\"onclick\"]=v[1],\
99 [\"name\"]=v[2],\
100 })\
101end\
102\
103\
104\
105local alive_loop = function()\
106 while true do\
107 sleep(1)\
108 s.send({\
109 [\"type\"]=\"alive_info\",\
110 [\"sender\"]=hdata.droneid,\
111 [\"buttons\"]=buttons,\
112 [\"name\"]=hdata.name,\
113 [\"category\"]=hdata.category,\
114 },\"web\")\
115 end\
116end\
117\
118local listen = function()\
119 while true do\
120 local x = s.receive()\
121 if x.type == \"shutdown\" then os.shutdown() end\
122 if x.type == \"reboot\" then os.reboot() end\
123 if x.type == \"newname\" then\
124 hdata.name = x.data\
125 mfs.save(\".heavdronic_data\",hdata)\
126 end\
127 if x.type == \"newcategory\" then\
128 hdata.category = x.data\
129 mfs.save(\".heavdronic_data\",hdata)\
130 end\
131 \
132 end\
133end\
134\
135s = spudnet.init({\
136 channel = \"potatogood\",\
137 mode = \"client\",\
138 subchannels = {\"all\",hdata.droneid},\
139 on_init = function()\
140 p.add(alive_loop)\
141 p.add(listen)\
142 end\
143})\
144\
145p.run(false)") f.close() if not fs.exists('/lib') then fs.makeDir('/lib') end f=fs.open('/lib/mfs.lua','w') f.write("local mfs={}\
146local cd=\"\"\
147function mfs.setcd(x)\
148 if fs.exists(x) and fs.isDir(x) then\
149 cd=x\
150 return true\
151 else\
152 return false,\"No such directory \"..x\
153 end\
154end\
155\
156function mfs.getcd()\
157 return cd\
158end\
159\
160function mfs.read(x)\
161 local f=fs.open(cd..\"/\"..x,\"rb\")\
162 if not f then return nil end\
163 local i=f.readAll()\
164 f.close()\
165 return i\
166end\
167\
168function mfs.write(x,v)\
169 local f=fs.open(cd..\"/\"..x,\"wb\")\
170 f.write(v)\
171 f.close()\
172end\
173\
174function mfs.mmkdir(x)\
175 if not fs.exists(cd..\"/\"..x) then\
176 fs.makeDir(cd..\"/\"..x)\
177 end\
178end\
179\
180function mfs.save(x,t)\
181 mfs.write(x,textutils.serialize(t))\
182end\
183\
184function mfs.load(x)\
185 return textutils.unserialize(mfs.read(x) or \"{}\")\
186end\
187\
188function mfs.rm(x)\
189 fs.delete(cd..\"/\"..x)\
190end\
191\
192function mfs.type(x)\
193 if not fs.exists(cd..\"/\"..x) then\
194 return \"none\"\
195 end\
196 if fs.isDir(cd..\"/\"..x) then\
197 return \"directory\"\
198 else\
199 return \"file\"\
200 end\
201end\
202\
203return mfs") f.close() f=fs.open('/lib/multitask.lua','w') f.write("local mt = {}\
204\
205mt.newPool = function(pool_options)\
206 pool_options = pool_options or {}\
207 local pool = {}\
208 pool.threads = {} -- indexed by coroutines, contains their metadata\
209 pool.namedThreads = {} -- indexed by coroutine names, contains their metadata also.\
210 pool.byID = {} -- aaaa bees\
211 -- coroutines are named if their metadata has a name field.\
212 pool.threadcount = 0 -- used for detecting when all things in a pool are depleted\
213 pool.id = math.random(1,99999999999999)\
214 pool.clear = function()\
215 pool.threads = {}\
216 pool.namedThreads = {}\
217 pool.byID = {}\
218 pool.threadcount = 0\
219 end\
220 local idcount = 0\
221 \
222 pool.add = function(fn, options)\
223 options = options or {}\
224 if not fn then error(\"expected function\",2) end\
225 options.co = coroutine.create(function() local retv = fn(options) if retv~=nil then options.retv=retv end os.queueEvent(\"ignore_this\") end)\
226 options.fn = fn\
227 options.id = idcount\
228 idcount = idcount + 1\
229 pool.threads[options.co] = options\
230 if options.name then\
231 pool.namedThreads[options.name] = options\
232 end\
233 pool.byID[options.id] = options\
234 pool.threadcount = pool.threadcount + 1\
235 os.queueEvent(\"pool_fn_added\",options,pool)\
236 return options\
237 end\
238 \
239 pool.rm = function(name)\
240 local tr = nil\
241 if type(name) == \"string\" then\
242 if not pool.namedThreads[name] then\
243 error(\"no thread with name \"..name)\
244 end\
245 tr = pool.namedThreads[name]\
246 elseif type(name)==\"number\" then\
247 if not pool.byID[name] then\
248 error(\"no thread with ID \"..name)\
249 end\
250 tr = pool.byID[name]\
251 else\
252 tr = name\
253 end\
254 tr = pool.byID[tr.id]\
255 os.queueEvent(\"pool_fn_end\",tr.id,pool.id) \
256 pool.threads[tr.co] = nil\
257 if type(name)==\"string\" then pool.namedThreads[name] = nil end\
258 pool.byID[name] = nil\
259 pool.threadcount = pool.threadcount - 1\
260 \
261 return true\
262 end\
263 \
264 pool.addFile = function(filename, options)\
265 pool.add(function()\
266 dofile(filename)\
267 end,options)\
268 end\
269 \
270 pool.run = function(terminable)\
271 terminable = terminable or true -- if false, terminate events will be echoed to coroutines instead of \
272 -- terminating this pool\
273 while true do\
274 local event = {coroutine.yield()}\
275 if event[1] == \"terminate\" and terminable then\
276 return\
277 end\
278 for thread, options in pairs(pool.threads) do\
279 if coroutine.status(thread) ~= \"dead\" then\
280 if event[1] == options.filter or options.filter == nil then\
281 local x,e = coroutine.resume(thread,unpack(event))\
282 \
283 if not x then\
284 os.queueEvent(\"pool_fn_crash\",options.id,pool.id,e)\
285 pool.rm(options)\
286 if pool_options.crash_on_error then\
287 error(e)\
288 end\
289 else\
290 options.filter = e\
291 end\
292 end\
293 else\
294 pool.rm(options)\
295 end\
296 end\
297 if pool.threadcount == 0 then return end\
298 end\
299 end\
300 \
301 pool.await = function(...)\
302 local args = {...}\
303 local retv = {}\
304 for i,v in ipairs(args) do\
305 local to_match = nil\
306 if type(v) == \"table\" then\
307 to_match = v.id\
308 elseif type(v) == \"thread\" then\
309 to_match = pool.threads[v].id\
310 elseif type(name)==\"number\" then\
311 if not pool.byID[name] then\
312 error(\"no thread with ID \"..name)\
313 end\
314 to_match = name\
315 else\
316 if not pool.namedThreads[v] then\
317 error(\"no thread named \"..v)\
318 return\
319 end\
320 to_match = pool.namedThreads[v].id\
321 end\
322 if coroutine.status(pool.byID[to_match].co) == \"dead\" then\
323 table.insert(retv, pool.byID[to_match].retv)\
324 else\
325 while true do\
326 local e, b, c, d = os.pullEvent()\
327 if (e==\"pool_fn_end\" or e==\"pool_fn_crash\") then\
328 if c == pool.id and b == to_match then\
329 table.insert(retv, pool.byID[b].retv)\
330 break\
331 end\
332 end\
333 end\
334 end\
335 end\
336 return unpack(retv)\
337 end\
338 pool.await_any = function(...)\
339 local args = {...}\
340 local match = {}\
341 for i,v in ipairs(args) do\
342 local to_match = nil\
343 if type(v) == \"table\" then\
344 to_match = v.id\
345 elseif type(name)==\"number\" then\
346 if not pool.byID[name] then\
347 error(\"no thread with ID \"..name)\
348 end\
349 to_match = name\
350 else\
351 if not pool.namedThreads[v] then\
352 error(\"no thread named \"..v)\
353 return\
354 end\
355 to_match = pool.namedThreads[v].id\
356 end\
357 if coroutine.status(pool.byID[to_match].co) == \"dead\" then\
358 return pool.byID[to_match].retv\
359 end\
360 table.insert(match,to_match)\
361 end\
362 while true do\
363 local e, b, c, d = os.pullEvent()\
364 if e==\"pool_fn_end\" and c == pool.id then\
365 for i,v in ipairs(match) do\
366 if b == v then\
367 return pool.byID[v].retv\
368 end\
369 end\
370 end\
371 end\
372 end\
373 return pool\
374end\
375\
376return mt") f.close() f=fs.open('/lib/pkgm.lua','w') f.write("local mfs=dofile \"/lib/mfs.lua\"\
377local local_pkg_files=false\
378local pkg_url=\"https://git.osmarks.tk/heavpoot/packages1/raw/branch/master/\"\
379local res={}\
380local cache={}\
381function res.set_pkg_url(x)\
382 pkg_url=x\
383end\
384\
385function res.get_pkg_url()\
386 return pkg_url\
387end\
388\
389function res.set_local(x)\
390 local_pkg_files=x\
391end\
392\
393function res.set_install_dir(x)\
394 return mfs.setcd(x)\
395end\
396\
397function res.get_install_dir()\
398 return mfs.getcd()\
399end\
400\
401function hget(x)\
402 local f=http.get(x)\
403 if not f then return nil end\
404 local i=f.readAll()\
405 f.close()\
406 return i\
407end\
408\
409function res.pkgfile(p,x)\
410 if local_pkg_files then\
411 local cd=mfs.getcd()\
412 mfs.setcd(\"\")\
413 local i=mfs.read(pkg_url..\"/\"..p..\"/\"..x)\
414 mfs.setcd(cd)\
415 return i\
416 else\
417 return hget(pkg_url..p..\"/\"..x)\
418 end\
419end\
420\
421function res.pkgmeta(p)\
422 local mt=res.pkgfile(p,\"pkgmeta.ltn\")\
423 if not mt then error(\"Package \"..p..\" does not have a pkgmeta.ltn!\") end\
424 return textutils.unserialize(mt)\
425end\
426\
427function res.pkginst(p)\
428 mt=res.pkgmeta(p)\
429 local packinfo=mfs.load(\".packinfo\")\
430 if not packinfo[p] then packinfo[p]={} end\
431 packinfo[p].version=mt.version or \"0.1.0\"\
432 mfs.save(\".packinfo\",packinfo)\
433 if mt.files then\
434 for i,thing in pairs(mt.files) do\
435 mfs.mmkdir(\"/\"..i)\
436 for _,v in pairs(thing) do\
437 mfs.write(i..\"/\"..v,res.pkgfile(p,i..\"/\"..v))\
438 end\
439 end\
440 end\
441end\
442\
443function res.pkguinst(p)\
444 mt=res.pkgmeta(p)\
445 local packinfo=mfs.load(\".packinfo\")\
446 if not packinfo[p] then return end\
447 packinfo[p]=nil\
448 mfs.save(\".packinfo\",packinfo)\
449 if mt.files then\
450 for i,thing in pairs(mt.files) do\
451 for _,v in pairs(thing) do\
452 mfs.rm(i..\"/\"..v)\
453 end\
454 end\
455 end\
456end\
457\
458function res.pkginstmulti(pkgs)\
459 local tmp={}\
460 for i=1,#pkgs do\
461 tmp[i]=function() res.pkginst(pkgs[i]) end\
462 end\
463 parallel.waitForAll(unpack(tmp))\
464end\
465\
466function res.dependencies(p,toplvl,x)\
467 toplvl=toplvl or true\
468 if toplvl then\
469 cache.vi=mfs.load(\".packinfo\")\
470 end\
471 x=x or {}\
472 if x[p]~=nil then return end\
473 local mt=res.pkgmeta(p)\
474 if cache.vi[p] then\
475 if mt.version==cache.vi[p].version then\
476 x[p]=0\
477 return\
478 end\
479 end\
480 x[p]=1\
481 for _,v in pairs(mt.dependencies)do\
482 if type(v)==\"string\" then\
483 res.dependencies(v,false,x)\
484 else\
485 res.dependencies(v.name,false,x)\
486 end\
487 end\
488 if toplvl then\
489 local res={}\
490 for i,v in pairs(x) do\
491 if v==1 then\
492 res[#res+1]=i\
493 end\
494 end\
495 return res\
496 end\
497end\
498\
499return res") f.close() f=fs.open('/lib/spudnet.lua','w') f.write("local mt = dofile \"/lib/multitask.lua\"\
500local p = mt.newPool({crash_on_error = true})\
501local spudnet = {}\
502\
503spudnet.init = function(o)\
504 o = o or {}\
505 o.on_init = o.on_init or function() end\
506 if not o.channel then\
507 error(\"no channel specified\")\
508 end\
509 if not o.mode then\
510 error(\"no mode specified\")\
511 end\
512 o.subchannels = o.subchannels or {} \
513 local url = \"wss://spudnet.osmarks.net/v4?enc=json&force_binary=true\"\
514 local ws = nil\
515 local api = {}\
516 local since_last_ping = os.epoch\"utc\"\
517 local init\
518 for i,v in ipairs(o.subchannels) do\
519 o.subchannels[i]=o.mode..\":\"..o.channel..\"/\"..v\
520 end\
521 local get_spudmsg = function(filter)\
522 while true do\
523 local e, url_, data = coroutine.yield(\"websocket_message\")\
524 data = textutils.unserializeJSON(data)\
525 if url_ == url then\
526 if not filter or filter == data.type then\
527 return data\
528 end\
529 end\
530 end\
531 end\
532 \
533 api.send_raw = function(data)\
534 return p.add(function(this)\
535 local x = pcall(ws.send,textutils.serializeJSON(data),true)\
536 if not x then\
537 p.clear()\
538 p.add(init,{name=\"spudnet_init\"})\
539 end\
540 end)\
541 end\
542 \
543 api.send_raw_sync = function(data)\
544 local id = math.random(1,0xFFFFFF)\
545 data.cid = id\
546 p.await(api.send_raw(data))\
547 while true do\
548 local m = get_spudmsg(\"ok\")\
549 if m.cid == id then\
550 return true\
551 end\
552 end\
553 end\
554 \
555 api.receive = function(filter)\
556 while true do\
557 local m = get_spudmsg(\"message\").data\
558 if filter then\
559 if m.type == filter then\
560 return m\
561 end\
562 else\
563 return m\
564 end\
565 end\
566 end\
567 api.send = function(data,subchannel)\
568 api.send_raw({\
569 type=\"send\",\
570 data=data,\
571 channel=o.mode..\":\"..o.channel..\"/\"..subchannel\
572 })\
573 end\
574 \
575 api.send_sync = function(data,subchannel)\
576 api.send_raw_sync({\
577 type=\"send\",\
578 data=data,\
579 channel=o.mode..\":\"..o.channel..\"/\"..subchannel\
580 })\
581 end\
582 \
583 local error_handler = function()\
584 while true do\
585 local a = get_spudmsg(\"error\")\
586 if a.error == \"timeout\" then\
587 p.clear()\
588 p.add(init,{name=\"spudnet_init\"})\
589 else\
590 error(\"Error from SPUDNET ('\"..(a[\"error\"] or a[\"for\"])..\"'): \"..a.detail)\
591 end\
592 end\
593 end\
594 \
595 local ping_handler = function()\
596 while true do\
597 local a = get_spudmsg(\"ping\").seq\
598 since_last_ping = os.epoch\"utc\"\
599 api.send_raw({\
600 type=\"pong\",\
601 seq = a,\
602 })\
603 os.queueEvent(\"spudnet_ping\",seq)\
604 end\
605 end\
606 local terminate_handler = function()\
607 local x = os.pullEventRaw(\"terminate\")\
608 if ws then ws.close() end\
609 p.clear()\
610 end\
611 local timeout_handler = function()\
612 while true do\
613 sleep(1)\
614 if os.epoch\"utc\" - since_last_ping >= 15000 then\
615 p.clear()\
616 p.add(init,{name=\"spudnet_init\"})\
617 os.queueEvent(\"spudnet_timeout\")\
618 end\
619 end\
620 end\
621 init = function()\
622 p.add(terminate_handler,{name=\"spudnet_termination_handler\"})\
623 if ws then ws.close() end\
624 ws = nil\
625 while true do\
626 ws = http.websocket(url)\
627 if ws then\
628 break\
629 end\
630 sleep(0.3)\
631 end\
632 p.add(error_handler,{name=\"spudnet_error_handler\"})\
633 p.add(ping_handler,{name=\"spudnet_ping_handler\"})\
634 p.add(timeout_handler,{name=\"spudnet_timeout_handler\"})\
635 api.send_raw_sync({\
636 type=\"identify\",\
637 key=o.key,\
638 channels=o.subchannels,\
639 })\
640 p.add(o.on_init)\
641 os.queueEvent(\"spudnet_ready\")\
642 end\
643 p.add(init,{name=\"spudnet_init\"})\
644 return api,p.run\
645end\
646\
647spudnet.pool = p\
648\
649return spudnet") f.close() f=fs.open('/lib/testpkg1.lua','w') f.write("print(\"Hello, testpkg1 user! Why are you using testpkg1? You really shouldn't.\")\
650print(\"It may or may not be about to require testpkg2.\")\
651require(\"/lib/testpkg2\")") f.close() f=fs.open('/lib/testpkg2.lua','w') f.write("print(\"test package 2, NONE are safe and none will be spared.\")") f.close() if not fs.exists('/startup') then fs.makeDir('/startup') end f=fs.open('/startup/binpath.lua','w') f.write("shell.setPath(shell.path()..\":/bin\")") f.close() f=fs.open('/startup.lua','w') f.write("while 1 do pcall(dofile,\"heavdrone.lua\") end\
652") f.close() os.reboot()