init.lua 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. -- Reference
  2. -- ports = get_real_portstates(pos): gets if inputs are powered from outside
  3. -- newport = merge_portstates(state1, state2): just does result = state1 or state2 for every port
  4. -- action_setports(pos, ports, vports): activates/deactivates the mesecons according to the portstates (helper for action)
  5. -- action(pos, ports): Applies new portstates to a luacontroller at pos
  6. -- lc_update(pos): updates the controller at pos by executing the code
  7. -- reset_meta (pos, code, errmsg): performs a software-reset, installs new code and prints error messages
  8. -- reset (pos): performs a hardware reset, turns off all ports
  9. --
  10. -- The Sandbox
  11. -- The whole code of the controller runs in a sandbox,
  12. -- a very restricted environment.
  13. -- However, as this does not prevent you from using e.g. loops,
  14. -- we need to check for these prohibited commands first.
  15. -- Actually the only way to damage the server is to
  16. -- use too much memory from the sandbox.
  17. -- You can add more functions to the environment
  18. -- (see where local env is defined)
  19. -- Something nice to play is is appending minetest.env to it.
  20. local BASENAME = "mesecons_luacontroller:luacontroller"
  21. local rules = {}
  22. rules.a = {x = -1, y = 0, z = 0, name="A"}
  23. rules.b = {x = 0, y = 0, z = 1, name="B"}
  24. rules.c = {x = 1, y = 0, z = 0, name="C"}
  25. rules.d = {x = 0, y = 0, z = -1, name="D"}
  26. ------------------
  27. -- Action stuff --
  28. ------------------
  29. -- These helpers are required to set the portstates of the luacontroller
  30. local get_real_portstates = function(pos) -- determine if ports are powered (by itself or from outside)
  31. ports = {
  32. a = mesecon:is_power_on(mesecon:addPosRule(pos, rules.a))
  33. and mesecon:rules_link(mesecon:addPosRule(pos, rules.a), pos),
  34. b = mesecon:is_power_on(mesecon:addPosRule(pos, rules.b))
  35. and mesecon:rules_link(mesecon:addPosRule(pos, rules.b), pos),
  36. c = mesecon:is_power_on(mesecon:addPosRule(pos, rules.c))
  37. and mesecon:rules_link(mesecon:addPosRule(pos, rules.c), pos),
  38. d = mesecon:is_power_on(mesecon:addPosRule(pos, rules.d))
  39. and mesecon:rules_link(mesecon:addPosRule(pos, rules.d), pos),
  40. }
  41. return ports
  42. end
  43. local merge_portstates = function (ports, vports)
  44. local npo = {a=false, b=false, c=false, d=false}
  45. npo.a = vports.a or ports.a
  46. npo.b = vports.b or ports.b
  47. npo.c = vports.c or ports.c
  48. npo.d = vports.d or ports.d
  49. return npo
  50. end
  51. local generate_name = function (ports, overwrite)
  52. local overwrite = overwrite or {}
  53. local d = overwrite.d or (ports.d and 1 or 0)
  54. local c = overwrite.c or (ports.c and 1 or 0)
  55. local b = overwrite.b or (ports.b and 1 or 0)
  56. local a = overwrite.a or (ports.a and 1 or 0)
  57. return BASENAME..d..c..b..a
  58. end
  59. local setport = function (pos, rule, ignore, state, ports)
  60. local ignorename = generate_name(ports, ignore)
  61. mesecon:swap_node(pos, ignorename)
  62. if state then
  63. mesecon:receptor_on(pos, {rule})
  64. else
  65. mesecon:receptor_off(pos, {rule})
  66. end
  67. if minetest.env:get_node(pos).name ~= ignorename then
  68. return true -- overridden by second process
  69. end
  70. return false -- success
  71. end
  72. local action = function (pos, ports, forcereset)
  73. local name = minetest.env:get_node(pos).name
  74. local vports = minetest.registered_nodes[name].virtual_portstates
  75. local newname = generate_name(ports)
  76. if name ~= newname and vports then
  77. local rules_on = {}
  78. local rules_off = {}
  79. local ignore = {}
  80. local interrupted
  81. if ports.a ~= vports.a then interrupted = setport(pos, rules.a, {a = 2}, ports.a, ports) end
  82. if interrupted and not forcereset then return end
  83. if ports.b ~= vports.b then interrupted = setport(pos, rules.b, {b = 2}, ports.b, ports) end
  84. if interrupted and not forcereset then return end
  85. if ports.c ~= vports.c then interrupted = setport(pos, rules.c, {c = 2}, ports.c, ports) end
  86. if interrupted and not forcereset then return end
  87. if ports.d ~= vports.d then interrupted = setport(pos, rules.d, {d = 2}, ports.d, ports) end
  88. if interrupted and not forcereset then return end
  89. mesecon:swap_node(pos, newname)
  90. end
  91. end
  92. local delayedaction = function (params)
  93. action(params.pos, params.ports)
  94. end
  95. --------------------
  96. -- Overheat stuff --
  97. --------------------
  98. local heat = function (meta) -- warm up
  99. h = meta:get_int("heat")
  100. if h ~= nil then
  101. meta:set_int("heat", h + 1)
  102. end
  103. end
  104. local cool = function (meta) -- cool down after a while
  105. h = meta:get_int("heat")
  106. if h ~= nil then
  107. meta:set_int("heat", h - 1)
  108. end
  109. end
  110. local overheat = function (meta) -- determine if too hot
  111. h = meta:get_int("heat")
  112. if h == nil then return true end -- if nil then overheat
  113. if h > 20 then
  114. return true
  115. else
  116. return false
  117. end
  118. end
  119. local overheat_off = function(pos)
  120. mesecon:receptor_off(pos, mesecon.rules.flat)
  121. end
  122. -------------------
  123. -- Parsing stuff --
  124. -------------------
  125. local code_prohibited = function(code)
  126. -- Clean code
  127. local prohibited = {"while", "for", "repeat", "until", "function"}
  128. for _, p in ipairs(prohibited) do
  129. if string.find(code, p) then
  130. return "Prohibited command: "..p
  131. end
  132. end
  133. end
  134. local safeprint = function(param)
  135. print(dump(param))
  136. end
  137. local interrupt = function(params)
  138. lc_update(params.pos, {type="interrupt", iid = params.iid})
  139. end
  140. local getinterrupt = function(pos)
  141. local interrupt = function (time, iid) -- iid = interrupt id
  142. if type(time) ~= "number" then return end
  143. local iid = iid or math.random()
  144. local meta = minetest.env:get_meta(pos)
  145. local interrupts = minetest.deserialize(meta:get_string("lc_interrupts")) or {}
  146. table.insert (interrupts, iid)
  147. meta:set_string("lc_interrupts", minetest.serialize(interrupts))
  148. minetest.after(time, interrupt, {pos=pos, iid = iid})
  149. end
  150. return interrupt
  151. end
  152. local getdigiline_send = function (pos)
  153. local digiline_send = function (channel, msg)
  154. if digiline then
  155. digiline:receptor_send(pos, digiline.rules.default, channel, msg)
  156. end
  157. end
  158. return digiline_send
  159. end
  160. local create_environment = function(pos, mem, event)
  161. -- Gather variables for the environment
  162. local vports = minetest.registered_nodes[minetest.env:get_node(pos).name].virtual_portstates
  163. vports = {a = vports.a, b = vports.b, c = vports.c, d = vports.d}
  164. local rports = get_real_portstates(pos)
  165. return { print = safeprint,
  166. pin = merge_portstates(vports, rports),
  167. port = vports,
  168. interrupt = getinterrupt(pos),
  169. digiline_send = getdigiline_send(pos),
  170. mem = mem,
  171. tostring = tostring,
  172. tonumber = tonumber,
  173. string = string,
  174. event = event}
  175. end
  176. local create_sandbox = function (code, env)
  177. -- Create Sandbox
  178. if code:byte(1) == 27 then
  179. return _, "You Hacker You! Don't use binary code!"
  180. end
  181. f, msg = loadstring(code)
  182. if not f then return _, msg end
  183. setfenv(f, env)
  184. return f
  185. end
  186. local do_overheat = function (pos, meta)
  187. -- Overheat protection
  188. heat(meta)
  189. minetest.after(0.5, cool, meta)
  190. if overheat(meta) then
  191. minetest.env:remove_node(pos)
  192. minetest.after(0.2, overheat_off, pos) -- wait for pending operations
  193. minetest.env:add_item(pos, BASENAME.."0000")
  194. return true
  195. end
  196. end
  197. local load_memory = function(meta)
  198. return minetest.deserialize(meta:get_string("lc_memory")) or {}
  199. end
  200. local save_memory = function(meta, mem)
  201. meta:set_string("lc_memory", minetest.serialize(mem))
  202. end
  203. local interrupt_allow = function (meta, event)
  204. if event.type ~= "interrupt" then return true end
  205. local interrupts = minetest.deserialize(meta:get_string("lc_interrupts")) or {}
  206. for _, i in ipairs(interrupts) do
  207. if minetest.serialize(i) == minetest.serialize(event.iid) then
  208. return true
  209. end
  210. end
  211. return false
  212. end
  213. local ports_invalid = function (var)
  214. if type(var) == "table" then
  215. return false
  216. end
  217. return "The ports you set are invalid"
  218. end
  219. ----------------------
  220. -- Parsing function --
  221. ----------------------
  222. lc_update = function (pos, event)
  223. local meta = minetest.env:get_meta(pos)
  224. if not interrupt_allow(meta, event) then return end
  225. if do_overheat(pos, meta) then return end
  226. -- load code & mem from memory
  227. local mem = load_memory(meta)
  228. local code = meta:get_string("code")
  229. -- make sure code is ok and create environment
  230. local prohibited = code_prohibited(code)
  231. if prohibited then return prohibited end
  232. local env = create_environment(pos, mem, event)
  233. -- create the sandbox and execute code
  234. local chunk, msg = create_sandbox (code, env)
  235. if not chunk then return msg end
  236. local success, msg = pcall(f)
  237. if not success then return msg end
  238. if ports_invalid(env.port) then return ports_invalid(env.port) end
  239. save_memory(meta, mem)
  240. -- Actually set the ports
  241. minetest.after(0, delayedaction, {pos = pos, ports = env.port})
  242. end
  243. local reset_meta = function(pos, code, errmsg)
  244. local meta = minetest.env:get_meta(pos)
  245. code = code or "";
  246. errmsg = errmsg or "";
  247. errmsg = string.gsub(errmsg, "%[", "(") -- would otherwise
  248. errmsg = string.gsub(errmsg, "%]", ")") -- corrupt formspec
  249. meta:set_string("code", code)
  250. meta:set_string("formspec", "size[10,8]"..
  251. "background[-0.2,-0.25;10.4,8.75;jeija_luac_background.png]"..
  252. "textarea[0.2,0.6;10.2,5;code;;"..code.."]"..
  253. "image_button[3.75,6;2.5,1;jeija_luac_runbutton.png;program;]"..
  254. "image_button_exit[9.72,-0.25;0.425,0.4;jeija_close_window.png;exit;]"..
  255. "label[0.1,5;"..errmsg.."]")
  256. meta:set_int("heat", 0)
  257. end
  258. local reset = function (pos)
  259. minetest.env:get_meta(pos):set_string("lc_interrupts", "")
  260. action(pos, {a=false, b=false, c=false, d=false}, true)
  261. end
  262. -- ______
  263. -- |
  264. -- | | |
  265. -- |___| | __ ___ _ __ _ _
  266. -- | | | | |\ | | |_| | | | | |_ |_|
  267. -- | |______ |__| | \| | | \ |__| |_ |_ |_ |\
  268. --
  269. -----------------------
  270. -- Node Registration --
  271. -----------------------
  272. local output_rules={}
  273. local input_rules={}
  274. local nodebox = {
  275. type = "fixed",
  276. fixed = {
  277. { -8/16, -8/16, -8/16, 8/16, -7/16, 8/16 }, -- bottom slab
  278. { -5/16, -7/16, -5/16, 5/16, -6/16, 5/16 }, -- circuit board
  279. { -3/16, -6/16, -3/16, 3/16, -5/16, 3/16 }, -- IC
  280. }
  281. }
  282. local selectionbox = {
  283. type = "fixed",
  284. fixed = { -8/16, -8/16, -8/16, 8/16, -5/16, 8/16 },
  285. }
  286. local digiline = {
  287. receptor = {},
  288. effector = {
  289. action = function (pos, node, channel, msg)
  290. lc_update (pos, {type = "digiline", channel = channel, msg = msg})
  291. end
  292. }
  293. }
  294. for a = 0, 2 do -- 0 = off; 1 = on; 2 = ignore
  295. for b = 0, 2 do
  296. for c = 0, 2 do
  297. for d = 0, 2 do
  298. local cid = tostring(d)..tostring(c)..tostring(b)..tostring(a)
  299. local nodename = BASENAME..cid
  300. local top = "jeija_luacontroller_top.png"
  301. if a == 1 then
  302. top = top.."^jeija_luacontroller_LED_A.png"
  303. end
  304. if b == 1 then
  305. top = top.."^jeija_luacontroller_LED_B.png"
  306. end
  307. if c == 1 then
  308. top = top.."^jeija_luacontroller_LED_C.png"
  309. end
  310. if d == 1 then
  311. top = top.."^jeija_luacontroller_LED_D.png"
  312. end
  313. if a + b + c + d ~= 0 then
  314. groups = {dig_immediate=2, not_in_creative_inventory=1}
  315. else
  316. groups = {dig_immediate=2}
  317. end
  318. output_rules[cid] = {}
  319. input_rules[cid] = {}
  320. if (a == 1) then table.insert(output_rules[cid], rules.a) end
  321. if (b == 1) then table.insert(output_rules[cid], rules.b) end
  322. if (c == 1) then table.insert(output_rules[cid], rules.c) end
  323. if (d == 1) then table.insert(output_rules[cid], rules.d) end
  324. if (a == 0) then table.insert(input_rules[cid], rules.a) end
  325. if (b == 0) then table.insert(input_rules[cid], rules.b) end
  326. if (c == 0) then table.insert(input_rules[cid], rules.c) end
  327. if (d == 0) then table.insert(input_rules[cid], rules.d) end
  328. local mesecons = {
  329. effector =
  330. {
  331. rules = input_rules[cid],
  332. action_change = function (pos, _, rulename, newstate)
  333. lc_update(pos, {type=newstate, pin=rulename})
  334. end,
  335. },
  336. receptor =
  337. {
  338. state = mesecon.state.on,
  339. rules = output_rules[cid]
  340. }
  341. }
  342. minetest.register_node(nodename, {
  343. description = "Luacontroller",
  344. drawtype = "nodebox",
  345. tiles = {
  346. top,
  347. "jeija_microcontroller_bottom.png",
  348. "jeija_microcontroller_sides.png",
  349. "jeija_microcontroller_sides.png",
  350. "jeija_microcontroller_sides.png",
  351. "jeija_microcontroller_sides.png"
  352. },
  353. paramtype = "light",
  354. groups = groups,
  355. drop = BASENAME.."0000",
  356. sunlight_propagates = true,
  357. selection_box = selectionbox,
  358. node_box = nodebox,
  359. on_construct = reset_meta,
  360. on_receive_fields = function(pos, formname, fields)
  361. reset(pos)
  362. reset_meta(pos, fields.code)
  363. local err = lc_update(pos, {type="program"})
  364. if err then print(err) end
  365. reset_meta(pos, fields.code, err)
  366. end,
  367. mesecons = mesecons,
  368. digiline = digiline,
  369. is_luacontroller = true,
  370. virtual_portstates = { a = a == 1, -- virtual portstates are
  371. b = b == 1, -- the ports the the
  372. c = c == 1, -- controller powers itself
  373. d = d == 1},-- so those that light up
  374. after_dig_node = function (pos, node)
  375. mesecon:receptor_off(pos, output_rules)
  376. end,
  377. })
  378. end
  379. end
  380. end
  381. end
  382. ------------------------
  383. -- Craft Registration --
  384. ------------------------
  385. minetest.register_craft({
  386. output = BASENAME.."0000 2",
  387. recipe = {
  388. {'mesecons_materials:silicon', 'mesecons_materials:silicon', 'group:mesecon_conductor_craftable'},
  389. {'mesecons_materials:silicon', 'mesecons_materials:silicon', 'group:mesecon_conductor_craftable'},
  390. {'group:mesecon_conductor_craftable', 'group:mesecon_conductor_craftable', ''},
  391. }
  392. })