util.lua 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. function mesecon.move_node(pos, newpos)
  2. local node = minetest.get_node(pos)
  3. local meta = minetest.get_meta(pos):to_table()
  4. minetest.remove_node(pos)
  5. minetest.set_node(newpos, node)
  6. minetest.get_meta(pos):from_table(meta)
  7. end
  8. -- Rules rotation Functions:
  9. function mesecon.rotate_rules_right(rules)
  10. local nr = {}
  11. for i, rule in ipairs(rules) do
  12. table.insert(nr, {
  13. x = -rule.z,
  14. y = rule.y,
  15. z = rule.x,
  16. name = rule.name})
  17. end
  18. return nr
  19. end
  20. function mesecon.rotate_rules_left(rules)
  21. local nr = {}
  22. for i, rule in ipairs(rules) do
  23. table.insert(nr, {
  24. x = rule.z,
  25. y = rule.y,
  26. z = -rule.x,
  27. name = rule.name})
  28. end
  29. return nr
  30. end
  31. function mesecon.rotate_rules_down(rules)
  32. local nr = {}
  33. for i, rule in ipairs(rules) do
  34. table.insert(nr, {
  35. x = -rule.y,
  36. y = rule.x,
  37. z = rule.z,
  38. name = rule.name})
  39. end
  40. return nr
  41. end
  42. function mesecon.rotate_rules_up(rules)
  43. local nr = {}
  44. for i, rule in ipairs(rules) do
  45. table.insert(nr, {
  46. x = rule.y,
  47. y = -rule.x,
  48. z = rule.z,
  49. name = rule.name})
  50. end
  51. return nr
  52. end
  53. --
  54. function mesecon.flattenrules(allrules)
  55. --[[
  56. {
  57. {
  58. {xyz},
  59. {xyz},
  60. },
  61. {
  62. {xyz},
  63. {xyz},
  64. },
  65. }
  66. --]]
  67. if allrules[1] and
  68. allrules[1].x then
  69. return allrules
  70. end
  71. local shallowrules = {}
  72. for _, metarule in ipairs( allrules) do
  73. for _, rule in ipairs(metarule ) do
  74. table.insert(shallowrules, rule)
  75. end
  76. end
  77. return shallowrules
  78. --[[
  79. {
  80. {xyz},
  81. {xyz},
  82. {xyz},
  83. {xyz},
  84. }
  85. --]]
  86. end
  87. function mesecon.rule2bit(findrule, allrules)
  88. --get the bit of the metarule the rule is in, or bit 1
  89. if (allrules[1] and
  90. allrules[1].x) or
  91. not findrule then
  92. return 1
  93. end
  94. for m,metarule in ipairs( allrules) do
  95. for _, rule in ipairs(metarule ) do
  96. if vector.equals(findrule, rule) then
  97. return m
  98. end
  99. end
  100. end
  101. end
  102. function mesecon.rule2metaindex(findrule, allrules)
  103. --get the metarule the rule is in, or allrules
  104. if allrules[1].x then
  105. return nil
  106. end
  107. if not(findrule) then
  108. return mesecon.flattenrules(allrules)
  109. end
  110. for m, metarule in ipairs( allrules) do
  111. for _, rule in ipairs(metarule ) do
  112. if vector.equals(findrule, rule) then
  113. return m
  114. end
  115. end
  116. end
  117. end
  118. function mesecon.rule2meta(findrule, allrules)
  119. if #allrules == 0 then return {} end
  120. local index = mesecon.rule2metaindex(findrule, allrules)
  121. if index == nil then
  122. if allrules[1].x then
  123. return allrules
  124. else
  125. return {}
  126. end
  127. end
  128. return allrules[index]
  129. end
  130. function mesecon.dec2bin(n)
  131. local x, y = math.floor(n / 2), n % 2
  132. if (n > 1) then
  133. return mesecon.dec2bin(x)..y
  134. else
  135. return ""..y
  136. end
  137. end
  138. function mesecon.getstate(nodename, states)
  139. for state, name in ipairs(states) do
  140. if name == nodename then
  141. return state
  142. end
  143. end
  144. error(nodename.." doesn't mention itself in "..dump(states))
  145. end
  146. function mesecon.getbinstate(nodename, states)
  147. return mesecon.dec2bin(mesecon.getstate(nodename, states)-1)
  148. end
  149. function mesecon.get_bit(binary,bit)
  150. bit = bit or 1
  151. local c = binary:len()-(bit-1)
  152. return binary:sub(c,c) == "1"
  153. end
  154. function mesecon.set_bit(binary,bit,value)
  155. if value == "1" then
  156. if not mesecon.get_bit(binary,bit) then
  157. return mesecon.dec2bin(tonumber(binary,2)+math.pow(2,bit-1))
  158. end
  159. elseif value == "0" then
  160. if mesecon.get_bit(binary,bit) then
  161. return mesecon.dec2bin(tonumber(binary,2)-math.pow(2,bit-1))
  162. end
  163. end
  164. return binary
  165. end
  166. function mesecon.invertRule(r)
  167. return vector.multiply(r, -1)
  168. end
  169. function mesecon.tablecopy(table) -- deep table copy
  170. if type(table) ~= "table" then return table end -- no need to copy
  171. local newtable = {}
  172. for idx, item in pairs(table) do
  173. if type(item) == "table" then
  174. newtable[idx] = mesecon.tablecopy(item)
  175. else
  176. newtable[idx] = item
  177. end
  178. end
  179. return newtable
  180. end
  181. function mesecon.cmpAny(t1, t2)
  182. if type(t1) ~= type(t2) then return false end
  183. if type(t1) ~= "table" and type(t2) ~= "table" then return t1 == t2 end
  184. for i, e in pairs(t1) do
  185. if not mesecon.cmpAny(e, t2[i]) then return false end
  186. end
  187. return true
  188. end
  189. -- does not overwrite values; number keys (ipairs) are appended, not overwritten
  190. function mesecon.mergetable(source, dest)
  191. local rval = mesecon.tablecopy(dest)
  192. for k, v in pairs(source) do
  193. rval[k] = dest[k] or mesecon.tablecopy(v)
  194. end
  195. for i, v in ipairs(source) do
  196. table.insert(rval, mesecon.tablecopy(v))
  197. end
  198. return rval
  199. end
  200. function mesecon.register_node(name, spec_common, spec_off, spec_on)
  201. spec_common.drop = spec_common.drop or name .. "_off"
  202. spec_common.on_blast = spec_common.on_blast or mesecon.on_blastnode
  203. spec_common.__mesecon_basename = name
  204. spec_on.__mesecon_state = "on"
  205. spec_off.__mesecon_state = "off"
  206. spec_on = mesecon.mergetable(spec_common, spec_on);
  207. spec_off = mesecon.mergetable(spec_common, spec_off);
  208. minetest.register_node(name .. "_on", spec_on)
  209. minetest.register_node(name .. "_off", spec_off)
  210. end
  211. -- swap onstate and offstate nodes, returns new state
  212. function mesecon.flipstate(pos, node)
  213. local nodedef = minetest.registered_nodes[node.name]
  214. local newstate
  215. if (nodedef.__mesecon_state == "on") then newstate = "off" end
  216. if (nodedef.__mesecon_state == "off") then newstate = "on" end
  217. minetest.swap_node(pos, {name = nodedef.__mesecon_basename .. "_" .. newstate,
  218. param2 = node.param2})
  219. return newstate
  220. end
  221. -- File writing / reading utilities
  222. local wpath = minetest.get_worldpath()
  223. function mesecon.file2table(filename)
  224. local f = io.open(wpath..DIR_DELIM..filename, "r")
  225. if f == nil then return {} end
  226. local t = f:read("*all")
  227. f:close()
  228. if t == "" or t == nil then return {} end
  229. return minetest.deserialize(t)
  230. end
  231. function mesecon.table2file(filename, table)
  232. local f = io.open(wpath..DIR_DELIM..filename, "w")
  233. f:write(minetest.serialize(table))
  234. f:close()
  235. end
  236. -- Block position "hashing" (convert to integer) functions for voxelmanip cache
  237. local BLOCKSIZE = 16
  238. -- convert node position --> block hash
  239. local function hash_blockpos(pos)
  240. return minetest.hash_node_position({
  241. x = math.floor(pos.x/BLOCKSIZE),
  242. y = math.floor(pos.y/BLOCKSIZE),
  243. z = math.floor(pos.z/BLOCKSIZE)
  244. })
  245. end
  246. -- Maps from a hashed mapblock position (as returned by hash_blockpos) to a
  247. -- table.
  248. --
  249. -- Contents of the table are:
  250. -- “vm” → the VoxelManipulator
  251. -- “va” → the VoxelArea
  252. -- “data” → the data array
  253. -- “param1” → the param1 array
  254. -- “param2” → the param2 array
  255. -- “dirty” → true if data has been modified
  256. --
  257. -- Nil if no VM-based transaction is in progress.
  258. local vm_cache = nil
  259. -- Starts a VoxelManipulator-based transaction.
  260. --
  261. -- During a VM transaction, calls to vm_get_node and vm_swap_node operate on a
  262. -- cached copy of the world loaded via VoxelManipulators. That cache can later
  263. -- be committed to the real map by means of vm_commit or discarded by means of
  264. -- vm_abort.
  265. function mesecon.vm_begin()
  266. vm_cache = {}
  267. end
  268. -- Finishes a VoxelManipulator-based transaction, freeing the VMs and map data
  269. -- and writing back any modified areas.
  270. function mesecon.vm_commit()
  271. for hash, tbl in pairs(vm_cache) do
  272. if tbl.dirty then
  273. local vm = tbl.vm
  274. vm:set_data(tbl.data)
  275. vm:write_to_map()
  276. vm:update_map()
  277. end
  278. end
  279. vm_cache = nil
  280. end
  281. -- Finishes a VoxelManipulator-based transaction, freeing the VMs and throwing
  282. -- away any modified areas.
  283. function mesecon.vm_abort()
  284. vm_cache = nil
  285. end
  286. -- Gets the cache entry covering a position, populating it if necessary.
  287. local function vm_get_or_create_entry(pos)
  288. local hash = hash_blockpos(pos)
  289. local tbl = vm_cache[hash]
  290. if not tbl then
  291. local vm = minetest.get_voxel_manip(pos, pos)
  292. local min_pos, max_pos = vm:get_emerged_area()
  293. local va = VoxelArea:new{MinEdge = min_pos, MaxEdge = max_pos}
  294. tbl = {vm = vm, va = va, data = vm:get_data(), param1 = vm:get_light_data(), param2 = vm:get_param2_data(), dirty = false}
  295. vm_cache[hash] = tbl
  296. end
  297. return tbl
  298. end
  299. -- Gets the node at a given position during a VoxelManipulator-based
  300. -- transaction.
  301. function mesecon.vm_get_node(pos)
  302. local tbl = vm_get_or_create_entry(pos)
  303. local index = tbl.va:indexp(pos)
  304. local node_value = tbl.data[index]
  305. if node_value == core.CONTENT_IGNORE then
  306. return nil
  307. else
  308. local node_param1 = tbl.param1[index]
  309. local node_param2 = tbl.param2[index]
  310. return {name = minetest.get_name_from_content_id(node_value), param1 = node_param1, param2 = node_param2}
  311. end
  312. end
  313. -- Sets a node’s name during a VoxelManipulator-based transaction.
  314. --
  315. -- Existing param1, param2, and metadata are left alone.
  316. function mesecon.vm_swap_node(pos, name)
  317. local tbl = vm_get_or_create_entry(pos)
  318. local index = tbl.va:indexp(pos)
  319. tbl.data[index] = minetest.get_content_id(name)
  320. tbl.dirty = true
  321. end
  322. -- Gets the node at a given position, regardless of whether it is loaded or
  323. -- not, respecting a transaction if one is in progress.
  324. --
  325. -- Outside a VM transaction, if the mapblock is not loaded, it is pulled into
  326. -- the server’s main map data cache and then accessed from there.
  327. --
  328. -- Inside a VM transaction, the transaction’s VM cache is used.
  329. function mesecon.get_node_force(pos)
  330. if vm_cache then
  331. return mesecon.vm_get_node(pos)
  332. else
  333. local node = minetest.get_node_or_nil(pos)
  334. if node == nil then
  335. -- Node is not currently loaded; use a VoxelManipulator to prime
  336. -- the mapblock cache and try again.
  337. minetest.get_voxel_manip(pos, pos)
  338. node = minetest.get_node_or_nil(pos)
  339. end
  340. return node
  341. end
  342. end
  343. -- Swaps the node at a given position, regardless of whether it is loaded or
  344. -- not, respecting a transaction if one is in progress.
  345. --
  346. -- Outside a VM transaction, if the mapblock is not loaded, it is pulled into
  347. -- the server’s main map data cache and then accessed from there.
  348. --
  349. -- Inside a VM transaction, the transaction’s VM cache is used.
  350. --
  351. -- This function can only be used to change the node’s name, not its parameters
  352. -- or metadata.
  353. function mesecon.swap_node_force(pos, name)
  354. if vm_cache then
  355. return mesecon.vm_swap_node(pos, name)
  356. else
  357. -- This serves to both ensure the mapblock is loaded and also hand us
  358. -- the old node table so we can preserve param2.
  359. local node = mesecon.get_node_force(pos)
  360. node.name = name
  361. minetest.swap_node(pos, node)
  362. end
  363. end
  364. -- Autoconnect Hooks
  365. -- Nodes like conductors may change their appearance and their connection rules
  366. -- right after being placed or after being dug, e.g. the default wires use this
  367. -- to automatically connect to linking nodes after placement.
  368. -- After placement, the update function will be executed immediately so that the
  369. -- possibly changed rules can be taken into account when recalculating the circuit.
  370. -- After digging, the update function will be queued and executed after
  371. -- recalculating the circuit. The update function must take care of updating the
  372. -- node at the given position itself, but also all of the other nodes the given
  373. -- position may have (had) a linking connection to.
  374. mesecon.autoconnect_hooks = {}
  375. -- name: A unique name for the hook, e.g. "foowire". Used to name the actionqueue function.
  376. -- fct: The update function with parameters function(pos, node)
  377. function mesecon.register_autoconnect_hook(name, fct)
  378. mesecon.autoconnect_hooks[name] = fct
  379. mesecon.queue:add_function("autoconnect_hook_"..name, fct)
  380. end
  381. function mesecon.execute_autoconnect_hooks_now(pos, node)
  382. for _, fct in pairs(mesecon.autoconnect_hooks) do
  383. fct(pos, node)
  384. end
  385. end
  386. function mesecon.execute_autoconnect_hooks_queue(pos, node)
  387. for name in pairs(mesecon.autoconnect_hooks) do
  388. mesecon.queue:add_action(pos, "autoconnect_hook_"..name, {node})
  389. end
  390. end