machine.lua 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. local modname = minetest.get_current_modname()
  2. local S = minetest.get_translator(modname)
  3. local MACHINE_TICK_INTERVAL = 0.5
  4. local MAX_MEMORY_RANGE = 512
  5. local tokenize = _G[modname].tokenize
  6. local parse = _G[modname].parse
  7. local compute_step = _G[modname].compute_step
  8. local node_formspec =
  9. {
  10. "size[8,7.5]",
  11. "label[2,0.5;" .. S("Program tape") .. "]",
  12. "list[context;main;2,1;1,1;]",
  13. nil,
  14. nil,
  15. "list[current_player;main;0,3.5;8,4;]",
  16. "listring[]",
  17. }
  18. local function get_node_formspec(running)
  19. node_formspec[4] = "label[4,0.5;" .. S("Current status:@n@1", running and S("running") or S("halted")) .. "]"
  20. node_formspec[5] = "button[4,1.3;1.7,1;" .. (running and "halt;" or "run;") ..
  21. (running and S("Halt") or S("Run")) .. "]"
  22. return table.concat(node_formspec)
  23. end
  24. local function halt(pos, meta)
  25. mesecon.receptor_off(pos)
  26. meta:set_string("formspec", get_node_formspec(false))
  27. end
  28. minetest.register_node(modname .. ":machine",
  29. {
  30. description = S("Turing machine"),
  31. drawtype = "mesh",
  32. mesh = modname .. "_machine.obj",
  33. tiles =
  34. {
  35. modname .. "_machine_base_horizontals.png",
  36. modname .. "_machine_base.png",
  37. modname .. "_machine_taperolls.png",
  38. modname .. "_machine_tapes.png"
  39. },
  40. groups = {deconstructable = 2},
  41. on_construct = function(pos)
  42. local meta = minetest.get_meta(pos)
  43. meta:set_string("formspec", get_node_formspec(false))
  44. local inv = meta:get_inventory()
  45. inv:set_size("main", 1)
  46. end,
  47. can_dig = function(pos)
  48. local inv = minetest.get_inventory({type = "node", pos = pos})
  49. return inv:is_empty("main")
  50. end,
  51. on_timer = function(pos, elapsed)
  52. local oc = os.clock()
  53. --TODO: do program caching stuff
  54. local meta = minetest.get_meta(pos)
  55. --fetch data from meta
  56. local ast = minetest.deserialize(meta:get_string("program"))
  57. local env = minetest.deserialize(meta:get_string("env"))
  58. local pc = minetest.deserialize(meta:get_string("pcs"))
  59. local instr = ast
  60. --process one step of the program
  61. --advance program counter
  62. pc[#pc] = pc[#pc] + 1
  63. --go to the loop we're in and find the current instruction
  64. for i, v in ipairs(pc)
  65. do
  66. instr = instr[v]
  67. end
  68. local powered = meta:get_int("mesecon_powered")
  69. local restart, output = compute_step(env, instr, pc, powered)
  70. if output == "wait"
  71. then
  72. if powered == 0
  73. then
  74. --save pc so we can continue after receiving a signal
  75. meta:set_string("pcs",minetest.serialize(pc))
  76. meta:set_int("wait_for_input", 1)
  77. return false
  78. end
  79. elseif output
  80. then
  81. if output > 0
  82. then
  83. mesecon.receptor_on(pos)
  84. else
  85. mesecon.receptor_off(pos)
  86. end
  87. end
  88. if not restart
  89. then
  90. halt(pos, meta)
  91. return false
  92. end
  93. --keep memory consumption in check
  94. if math.abs(env.headpos) > MAX_MEMORY_RANGE
  95. then
  96. halt(pos, meta)
  97. return false --TODO: show some sign of error
  98. end
  99. restart = restart * MACHINE_TICK_INTERVAL
  100. --writeback
  101. meta:set_string("pcs",minetest.serialize(pc))
  102. meta:set_string("env", minetest.serialize(env))
  103. --minetest.chat_send_all(os.clock() - oc)
  104. --we don't use auto restart by returning true so we can implement
  105. --the | (wait) instruction
  106. local timer = minetest.get_node_timer(pos)
  107. timer:start(restart)
  108. return false
  109. end,
  110. on_receive_fields = function(pos, formname, fields, sender)
  111. local meta = minetest.get_meta(pos)
  112. local timer = minetest.get_node_timer(pos)
  113. local timer_started = timer:is_started()
  114. if fields.run and not timer_started
  115. then
  116. local inv = meta:get_inventory()
  117. local itemstack = inv:get_stack("main", 1)
  118. if not itemstack:is_empty() and itemstack:get_name() == modname .. ":tape"
  119. then
  120. do
  121. --parse program
  122. local imeta = itemstack:get_meta()
  123. local program = imeta:get_string("program")
  124. local tkns = tokenize(program)
  125. local ast = parse(tkns)
  126. if ast
  127. then
  128. meta:set_string("pcs",minetest.serialize({0})) --initialize program counter
  129. meta:set_string("program", minetest.serialize(ast)) --write program into meta
  130. meta:set_string("env", minetest.serialize({headpos = 1})) --write tape into meta
  131. end
  132. end
  133. collectgarbage()
  134. --start node timer
  135. local timer = minetest.get_node_timer(pos)
  136. timer:start(MACHINE_TICK_INTERVAL)
  137. meta:set_string("formspec", get_node_formspec(true))
  138. end
  139. elseif fields.halt and timer_started
  140. then
  141. --stop node timer
  142. timer:stop()
  143. halt(pos, meta)
  144. end
  145. end,
  146. on_metadata_inventory_take = function(pos, listname, index, stack, player)
  147. local timer = minetest.get_node_timer(pos)
  148. timer:stop()
  149. local meta = minetest.get_meta(pos)
  150. halt(pos, meta)
  151. end,
  152. mesecons =
  153. {
  154. receptor =
  155. {
  156. state = mesecon.state.off
  157. },
  158. effector =
  159. {
  160. action_on = function(pos, node)
  161. local meta = minetest.get_meta(pos)
  162. meta:set_int("mesecon_powered", 1)
  163. if meta:get_int("wait_for_input") == 1
  164. then
  165. meta:set_int("wait_for_input", 0)
  166. local def = minetest.registered_nodes[node.name]
  167. def.on_timer(pos, 0)
  168. end
  169. end,
  170. action_off = function(pos, node)
  171. local meta = minetest.get_meta(pos)
  172. meta:set_int("mesecon_powered", 0)
  173. end,
  174. },
  175. },
  176. })