network.lua 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. --[[
  2. Hyperloop Mod
  3. =============
  4. Copyright (C) 2017-2019 Joachim Stolberg
  5. LGPLv2.1+
  6. See LICENSE.txt for more information
  7. Station and elevator network management
  8. ]]--
  9. -- for lazy programmers
  10. local S = function(pos) if pos then return minetest.pos_to_string(pos) end end
  11. local P = minetest.string_to_pos
  12. local M = minetest.get_meta
  13. -- Convert to list and add pos based on key string
  14. local function table_to_list(table)
  15. local lRes = {}
  16. for key,item in pairs(table) do
  17. item.pos = P(key)
  18. lRes[#lRes+1] = item
  19. end
  20. return lRes
  21. end
  22. local function distance(pos1, pos2)
  23. return math.floor(math.abs(pos1.x - pos2.x) +
  24. math.abs(pos1.y - pos2.y) + math.abs(pos1.z - pos2.z))
  25. end
  26. -- Add the distance to pos to each list item
  27. local function add_distance_to_list(lStations, pos)
  28. for _,item in ipairs(lStations) do
  29. item.distance = distance(item.pos, pos)
  30. end
  31. return lStations
  32. end
  33. -- Add the index to each list item
  34. local function add_index_to_list(lStations)
  35. -- walk through the list of floors for the next connection
  36. local get_next = function(key, idx)
  37. for _,floor in ipairs(lStations) do
  38. if floor.conn[6] == key then -- upward match?
  39. floor.idx = idx
  40. return S(floor.pos) -- return floor key
  41. end
  42. end
  43. end
  44. local key = nil
  45. for idx = 1,#lStations do
  46. key = get_next(key, idx)
  47. end
  48. return lStations
  49. end
  50. -- Return a table with all stations, the given station (as 'sKey') is connected with
  51. -- tRes is used for the resulting table (recursive call)
  52. local function get_stations(tStations, sKey, tRes)
  53. if not tStations[sKey] or not tStations[sKey].conn then
  54. return {}
  55. end
  56. for dir,dest in pairs(tStations[sKey].conn) do
  57. -- Not already visited?
  58. if not tRes[dest] then
  59. -- Known station?
  60. if tStations[dest] then
  61. tStations[dest].name = tStations[dest].name or ""
  62. tRes[dest] = tStations[dest]
  63. get_stations(tStations, dest, tRes)
  64. end
  65. end
  66. end
  67. return tRes
  68. end
  69. -- Return a list with sorted elevators, beginning with the top car
  70. -- with no shaft upwards
  71. local function sort_based_on_level(tStations)
  72. local lStations = table_to_list(table.copy(tStations))
  73. -- to be able to sort the list, an index has to be added
  74. lStations = add_index_to_list(lStations)
  75. table.sort(lStations, function(a,b) return (a.idx or 9999) < (b.idx or 9999) end)
  76. return lStations
  77. end
  78. -- Return a list with sorted stations
  79. local function sort_based_on_distance(tStations, pos)
  80. local lStations = table_to_list(table.copy(tStations))
  81. -- to be able to sort the list, the distance to pos has to be added
  82. lStations = add_distance_to_list(lStations, pos)
  83. table.sort(lStations, function(a,b) return a.distance < b.distance end)
  84. return lStations
  85. end
  86. -- Return a list with sorted stations
  87. local function sort_based_on_name(tStations, pos)
  88. local lStations = table_to_list(table.copy(tStations))
  89. -- Add distance
  90. lStations = add_distance_to_list(lStations, pos)
  91. table.sort(lStations, function(a,b) return a.name < b.name end)
  92. return lStations
  93. end
  94. --
  95. -- Class Network
  96. --
  97. --[[
  98. tStations["(x,y,z)"] = {
  99. ["conn"] = {
  100. dir = "(200,0,20)",
  101. },
  102. }
  103. change_counter = n,
  104. ]]--
  105. local Network = {}
  106. vents.Network = Network
  107. function Network:new()
  108. local o = {
  109. tStations = {},
  110. change_counter = 0,
  111. }
  112. setmetatable(o, self)
  113. self.__index = self
  114. return o
  115. end
  116. -- Set an elevator or station entry.
  117. -- tAttr is a table with additional attributes to be stored.
  118. function Network:set(pos, name, tAttr)
  119. if pos then
  120. local sKey = S(pos)
  121. if not self.tStations[sKey] then
  122. self.tStations[sKey] = {
  123. conn = {},
  124. }
  125. end
  126. self.tStations[sKey].name = name or ""
  127. for k,v in pairs(tAttr) do
  128. self.tStations[sKey][k] = v
  129. end
  130. self.change_counter = self.change_counter + 1
  131. end
  132. end
  133. -- Update an elevator or station entry.
  134. -- tAttr is a table with additional attributes to be stored.
  135. function Network:update(pos, tAttr)
  136. if pos then
  137. local sKey = S(pos)
  138. if self.tStations[sKey] then
  139. for k,v in pairs(tAttr) do
  140. if v == "nil" then
  141. self.tStations[sKey][k] = nil
  142. else
  143. self.tStations[sKey][k] = v
  144. end
  145. end
  146. self.change_counter = self.change_counter + 1
  147. end
  148. end
  149. end
  150. function Network:get(pos)
  151. return pos and self.tStations[S(pos)]
  152. end
  153. -- Delete an elevator or station entry.
  154. function Network:delete(pos)
  155. if pos then
  156. self.tStations[S(pos)] = nil
  157. self.change_counter = self.change_counter + 1
  158. end
  159. end
  160. function Network:changed(counter)
  161. return self.change_counter > counter, self.change_counter
  162. end
  163. -- Update the connection data base. The output dir information is needed
  164. -- to be able to delete a connection, if necessary.
  165. -- Returns true, if data base is changed.
  166. function Network:update_connections(pos, out_dir, conn_pos)
  167. local sKey = S(pos)
  168. local res = false
  169. if not self.tStations[sKey] then
  170. self.tStations[sKey] = {}
  171. res = true
  172. end
  173. if not self.tStations[sKey].conn then
  174. self.tStations[sKey].conn = {}
  175. res = true
  176. end
  177. conn_pos = S(conn_pos)
  178. if self.tStations[sKey].conn[out_dir] ~= conn_pos then
  179. self.tStations[sKey].conn[out_dir] = conn_pos
  180. res = true
  181. end
  182. if res then
  183. self.change_counter = self.change_counter + 1
  184. end
  185. return res
  186. end
  187. -- Return the nearest station position
  188. function Network:get_next_station(pos)
  189. local min_dist = 999999
  190. local min_key = nil
  191. local dist
  192. for key,item in pairs(self.tStations) do
  193. if not item.junction then
  194. dist = distance(pos, P(key))
  195. if dist < min_dist then
  196. min_dist = dist
  197. min_key = key
  198. end
  199. end
  200. end
  201. return P(min_key)
  202. end
  203. -- Return a sorted list of stations
  204. -- Param pos: player pos
  205. -- Param station_pos: next station pos or nil.
  206. -- Used to generate list with connected stations only
  207. -- Param sorted: either "dist" or "level"
  208. function Network:station_list(pos, station_pos, sorted)
  209. local tStations, lStations
  210. if station_pos then
  211. local tRes = {}
  212. tStations = get_stations(self.tStations, S(station_pos), tRes) -- reduced
  213. else
  214. tStations = self.tStations -- all stations
  215. end
  216. if sorted == "dist" then
  217. lStations = sort_based_on_distance(tStations, pos)
  218. elseif sorted == "level" then
  219. lStations = sort_based_on_level(tStations)
  220. else
  221. -- delete own station from list
  222. tStations[S(station_pos)] = nil
  223. lStations = sort_based_on_name(tStations, pos)
  224. end
  225. return lStations
  226. end
  227. -- Check the complete table by means of the provided callback bool = func(pos)
  228. function Network:filter(callback)
  229. local lKeys = {}
  230. for key,_ in pairs(self.tStations) do
  231. lKeys[#lKeys+1] = key
  232. end
  233. for _,key in ipairs(lKeys) do
  234. if not callback(P(key)) then
  235. self.tStations[key] = nil
  236. end
  237. end
  238. end
  239. function Network:deserialize(data)
  240. if data ~= "" then
  241. data = minetest.deserialize(data)
  242. self.tStations = data.tStations
  243. self.change_counter = data.change_counter
  244. end
  245. end
  246. function Network:serialize()
  247. return minetest.serialize(self)
  248. end
  249. -- Return a pos/item table with all network nodes, the node at pos is connected with
  250. function Network:get_node_table(pos)
  251. local tRes = {}
  252. local key = S(pos)
  253. get_stations(self.tStations, key, tRes)
  254. tRes[key] = nil
  255. return tRes
  256. end