init.lua 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. if not minetest.global_exists("networks") then
  2. networks = {}
  3. networks.modpath = minetest.get_modpath("networks")
  4. -- load insecure environment
  5. local secenv = minetest.request_insecure_environment()
  6. if secenv then
  7. print("[networks] insecure environment loaded.")
  8. local success, lib = pcall(secenv.require, "lsqlite3")
  9. if success then
  10. networks.sql = lib
  11. assert(networks.sql)
  12. assert(networks.sql.open)
  13. else
  14. minetest.log("error", "could not find sqlite3. machine networks will not function")
  15. minetest.log("error", lib)
  16. end
  17. else
  18. minetest.log("error", "[networks] failed to load insecure" ..
  19. " environment, please add this mod to the trusted mods list.")
  20. end
  21. end
  22. local param2_rules = {
  23. [0] = "x",
  24. [1] = "z",
  25. [2] = "x",
  26. [3] = "z",
  27. [4] = "x",
  28. [5] = "y",
  29. [6] = "x",
  30. [7] = "y",
  31. [8] = "x",
  32. [9] = "y",
  33. [10] = "x",
  34. [11] = "y",
  35. [12] = "y",
  36. [13] = "z",
  37. [14] = "y",
  38. [15] = "z",
  39. [16] = "y",
  40. [17] = "z",
  41. [18] = "y",
  42. [19] = "z",
  43. [20] = "x",
  44. [21] = "z",
  45. [22] = "x",
  46. [23] = "z",
  47. }
  48. local direction_rules = {
  49. ["n"] = "z",
  50. ["s"] = "z",
  51. ["w"] = "x",
  52. ["e"] = "x",
  53. ["u"] = "y",
  54. ["d"] = "y",
  55. }
  56. -- Get cable axis alignment from a param2 value.
  57. -- Cable nodes must have nodeboxes that match this.
  58. -- All cable nodes must align to the same axis.
  59. networks.cable_rotation_to_axis =
  60. function(param2)
  61. if param2 >= 0 and param2 <= 23 then
  62. return param2_rules[param2]
  63. end
  64. end
  65. -- Get a unit vector from a cardinal direction, or up/down.
  66. networks.direction_to_vector =
  67. function(dir)
  68. local d = vector.new(0, 0, 0)
  69. if dir == "n" then
  70. d.z = 1
  71. elseif dir == "s" then
  72. d.z = -1
  73. elseif dir == "w" then
  74. d.x = -1
  75. elseif dir == "e" then
  76. d.x = 1
  77. elseif dir == "u" then
  78. d.y = 1
  79. elseif dir == "d" then
  80. d.y = -1
  81. else
  82. return nil
  83. end
  84. return d
  85. end
  86. -- Find a network hub, starting from a position and continuing in a direction.
  87. -- Cable nodes (with the right axis) may intervene between hubs.
  88. -- This function must return the position of the next hub, if possible, or nil.
  89. networks.find_hub =
  90. function(pos, dir, tier)
  91. local meta = minetest.get_meta(pos)
  92. local p = vector.new(pos.x, pos.y, pos.z)
  93. local d = networks.direction_to_vector(dir)
  94. local station_name = "switching_station:" .. tier
  95. local cable_name = "cable:" .. tier
  96. -- Max cable length. +1 because a switching station takes up 1 meter of length.
  97. local cable_length = cable.get_max_length(tier)+1
  98. -- Seek a limited number of meters in a direction.
  99. for i = 1, cable_length, 1 do
  100. p = vector.add(p, d)
  101. local node = minetest.get_node(p)
  102. if node.name == station_name then
  103. -- Compatible switching station found!
  104. return p
  105. elseif node.name == cable_name then
  106. -- It's a cable node. We need to check its rotation.
  107. local paxis = networks.cable_rotation_to_axis(node.param2)
  108. if paxis then
  109. local daxis = direction_rules[dir]
  110. if not daxis or paxis ~= daxis then
  111. -- Cable has bad axis. Stop scanning.
  112. return nil
  113. end
  114. else
  115. -- Invalid param2. We stop scanning.
  116. return nil
  117. end
  118. -- Unless these items can automatically update switching stations when removed, we can't allow this.
  119. --elseif minetest.get_item_group(node.name, "conductor") > 0 and minetest.get_item_group(node.name, "block") > 0 then
  120. -- Anything that is both in group `conductor` and `block` is treated as a cable node.
  121. -- This allows cables to pass through walls without requiring ugly holes.
  122. else
  123. -- Anything other than a cable node or switching station blocks search.
  124. return nil
  125. end
  126. end
  127. return nil
  128. end
  129. networks.refresh_hubs =
  130. function(pos, tier)
  131. local meta = minetest.get_meta(pos)
  132. for k, v in ipairs({
  133. {n="n"},
  134. {n="s"},
  135. {n="e"},
  136. {n="w"},
  137. {n="u"},
  138. {n="d"},
  139. }) do
  140. local p = networks.find_hub(pos, v.n, tier)
  141. if p then
  142. meta:set_string(v.n, minetest.pos_to_string(p))
  143. else
  144. meta:set_string(v.n, nil)
  145. end
  146. end
  147. end
  148. -- Invalidate all nearby hubs from a position: NSEW, UD.
  149. -- This should cause them to re-update their routing.
  150. -- This would need to be done if a hub or cable is removed.
  151. networks.invalidate_hubs =
  152. function(pos, tier)
  153. for k, v in ipairs({
  154. {n="n"},
  155. {n="s"},
  156. {n="e"},
  157. {n="w"},
  158. {n="u"},
  159. {n="d"},
  160. }) do
  161. local p = networks.find_hub(pos, v.n, tier)
  162. if p then
  163. local meta = minetest.get_meta(p)
  164. for i, j in ipairs({
  165. {n="n"},
  166. {n="s"},
  167. {n="e"},
  168. {n="w"},
  169. {n="u"},
  170. {n="d"},
  171. }) do
  172. meta:set_string(j.n, nil)
  173. end
  174. -- Trigger node update.
  175. local timer = minetest.get_node_timer(p)
  176. if not timer:is_started() then
  177. timer:start(1.0)
  178. end
  179. end
  180. end
  181. end
  182. -- This function must return a table of all adjacent hubs.
  183. -- Table entries shall contain position of hub and its message function.
  184. networks.get_adjacent_hubs =
  185. function(pos, tiers)
  186. -- If `tiers` is omitted or nil, then all tiers are allowed.
  187. if not tiers then tiers = {"lv", "mv", "hv"} end
  188. -- Return list of discovered network hubs.
  189. local hubs = {}
  190. -- List of valid adjacent locations.
  191. local targets = {
  192. {x=pos.x+1, y=pos.y, z=pos.z},
  193. {x=pos.x-1, y=pos.y, z=pos.z},
  194. {x=pos.x, y=pos.y, z=pos.z+1},
  195. {x=pos.x, y=pos.y, z=pos.z-1},
  196. {x=pos.x, y=pos.y-1, z=pos.z},
  197. {x=pos.x, y=pos.y+1, z=pos.z},
  198. }
  199. -- Get all adjacent nodes once.
  200. local nodes = {}
  201. for k, v in ipairs(targets) do
  202. local meta = minetest.get_meta(v)
  203. if meta:get_string("technic_machine") == "yes" then
  204. local nn = meta:get_string("technic_name")
  205. if meta:get_string("technic_type") == "switch" then
  206. nodes[#nodes+1] = {name=nn, pos=v}
  207. end
  208. end
  209. end
  210. -- Scan through adjacent nodes and find valid ones.
  211. for j, t in ipairs(tiers) do
  212. local nn = "switching_station:" .. t
  213. local def = minetest.registered_items[nn]
  214. for k, v in ipairs(nodes) do
  215. if v.name == nn then
  216. hubs[#hubs+1] = {pos=v.pos, on_machine_execute=def.on_machine_execute}
  217. end
  218. end
  219. end
  220. return hubs
  221. end
  222. -- This function must return a table of all adjacent machines.
  223. -- Table entries shall contain position of machine and its message function.
  224. networks.get_adjacent_machines =
  225. function(pos, tier)
  226. -- Return list of discovered adjacent machines.
  227. local hubs = {}
  228. -- List of valid adjacent locations.
  229. local targets = {
  230. {x=pos.x+1, y=pos.y, z=pos.z},
  231. {x=pos.x-1, y=pos.y, z=pos.z},
  232. {x=pos.x, y=pos.y, z=pos.z+1},
  233. {x=pos.x, y=pos.y, z=pos.z-1},
  234. {x=pos.x, y=pos.y-1, z=pos.z},
  235. {x=pos.x, y=pos.y+1, z=pos.z},
  236. }
  237. for k, v in ipairs(targets) do
  238. local meta = minetest.get_meta(v)
  239. if meta:get_string("technic_machine") == "yes" then
  240. if string.find(meta:get_string("technic_tier"), tier) then
  241. local nn = meta:get_string("technic_name")
  242. -- Switching stations are NOT machines in the context of this function.
  243. -- This function cannot return switching stations because that would make a recursion mess.
  244. if nn ~= "" and not string.find(nn, "^switching_station:") then
  245. local def = minetest.registered_items[nn]
  246. assert(type(def.on_machine_execute) == "function")
  247. hubs[#hubs+1] = {pos=v, on_machine_execute=def.on_machine_execute}
  248. end
  249. end
  250. end
  251. end
  252. return hubs
  253. end
  254. if not networks.run_once then
  255. local c = "networks:core"
  256. local f = networks.modpath .. "/init.lua"
  257. reload.register_file(c, f, false)
  258. dofile(networks.modpath .. "/nodestore.lua")
  259. dofile(networks.modpath .. "/net2.lua")
  260. networks.run_once = true
  261. end