You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

380 lines
10KB

  1. local max_entity_id = 1000000000000 -- If you need more, there's a problem with your code
  2. local luaentity = {}
  3. pipeworks.luaentity = luaentity
  4. luaentity.registered_entities = {}
  5. local filename = minetest.get_worldpath().."/luaentities"
  6. local function read_file()
  7. local f = io.open(filename, "r")
  8. if f == nil then return {} end
  9. local t = f:read("*all")
  10. f:close()
  11. if t == "" or t == nil then return {} end
  12. return minetest.deserialize(t) or {}
  13. end
  14. local function write_file(tbl)
  15. local f = io.open(filename, "w")
  16. f:write(minetest.serialize(tbl))
  17. f:close()
  18. end
  19. local function read_entities()
  20. local t = read_file()
  21. for _, entity in pairs(t) do
  22. local x=entity.start_pos.x
  23. local y=entity.start_pos.y
  24. local z=entity.start_pos.z
  25. x=math.max(-30912,x)
  26. y=math.max(-30912,y)
  27. z=math.max(-30912,z)
  28. x=math.min(30927,x)
  29. y=math.min(30927,y)
  30. z=math.min(30927,z)
  31. entity.start_pos.x = x
  32. entity.start_pos.y = y
  33. entity.start_pos.z = z
  34. setmetatable(entity, luaentity.registered_entities[entity.name])
  35. end
  36. return t
  37. end
  38. local function write_entities()
  39. if not luaentity.entities then
  40. -- This can happen if crashing on startup, causing another error that
  41. -- masks the original one. Return gracefully in that case instead.
  42. return
  43. end
  44. for _, entity in pairs(luaentity.entities) do
  45. setmetatable(entity, nil)
  46. for _, attached in pairs(entity._attached_entities) do
  47. if attached.entity then
  48. attached.entity:remove()
  49. attached.entity = nil
  50. end
  51. end
  52. entity._attached_entities_master = nil
  53. end
  54. write_file(luaentity.entities)
  55. end
  56. minetest.register_on_shutdown(write_entities)
  57. luaentity.entities_index = 0
  58. local function get_blockpos(pos)
  59. return {x = math.floor(pos.x / 16),
  60. y = math.floor(pos.y / 16),
  61. z = math.floor(pos.z / 16)}
  62. end
  63. local active_blocks = {} -- These only contain active blocks near players (i.e., not forceloaded ones)
  64. local move_entities_globalstep_part1 = function(dtime)
  65. local active_block_range = tonumber(minetest.settings:get("active_block_range")) or 2
  66. local new_active_blocks = {}
  67. for _, player in ipairs(minetest.get_connected_players()) do
  68. local blockpos = get_blockpos(player:get_pos())
  69. local minp = vector.subtract(blockpos, active_block_range)
  70. local maxp = vector.add(blockpos, active_block_range)
  71. for x = minp.x, maxp.x do
  72. for y = minp.y, maxp.y do
  73. for z = minp.z, maxp.z do
  74. local pos = {x = x, y = y, z = z}
  75. new_active_blocks[minetest.hash_node_position(pos)] = pos
  76. end
  77. end
  78. end
  79. end
  80. active_blocks = new_active_blocks
  81. -- todo: callbacks on block load/unload
  82. end
  83. local function is_active(pos)
  84. return active_blocks[minetest.hash_node_position(get_blockpos(pos))] ~= nil
  85. end
  86. local entitydef_default = {
  87. _attach = function(self, attached, attach_to)
  88. local attached_def = self._attached_entities[attached]
  89. local attach_to_def = self._attached_entities[attach_to]
  90. attached_def.entity:set_attach(
  91. attach_to_def.entity, "",
  92. vector.subtract(attached_def.offset, attach_to_def.offset), -- todo: Does not work because is object space
  93. vector.new(0, 0, 0)
  94. )
  95. end,
  96. _set_master = function(self, index)
  97. self._attached_entities_master = index
  98. if not index then
  99. return
  100. end
  101. local def = self._attached_entities[index]
  102. if not def.entity then
  103. return
  104. end
  105. def.entity:set_pos(vector.add(self._pos, def.offset))
  106. def.entity:set_velocity(self._velocity)
  107. def.entity:set_acceleration(self._acceleration)
  108. end,
  109. _attach_all = function(self)
  110. local master = self._attached_entities_master
  111. if not master then
  112. return
  113. end
  114. for id, entity in pairs(self._attached_entities) do
  115. if id ~= master and entity.entity then
  116. self:_attach(id, master)
  117. end
  118. end
  119. end,
  120. _detach_all = function(self)
  121. local master = self._attached_entities_master
  122. for id, entity in pairs(self._attached_entities) do
  123. if id ~= master and entity.entity then
  124. entity.entity:set_detach()
  125. end
  126. end
  127. end,
  128. _add_attached = function(self, index)
  129. local entity = self._attached_entities[index]
  130. if entity.entity then
  131. return
  132. end
  133. local entity_pos = vector.add(self._pos, entity.offset)
  134. if not is_active(entity_pos) then
  135. return
  136. end
  137. local ent = minetest.add_entity(entity_pos, entity.name):get_luaentity()
  138. ent:from_data(entity.data)
  139. ent.parent_id = self._id
  140. ent.attached_id = index
  141. entity.entity = ent.object
  142. local master = self._attached_entities_master
  143. if master then
  144. self:_attach(index, master)
  145. else
  146. self:_set_master(index)
  147. end
  148. end,
  149. _remove_attached = function(self, index)
  150. local master = self._attached_entities_master
  151. local entity = self._attached_entities[index]
  152. local ent = entity and entity.entity
  153. if entity then entity.entity = nil end
  154. if index == master then
  155. self:_detach_all()
  156. local newmaster
  157. for id, attached in pairs(self._attached_entities) do
  158. if id ~= master and attached.entity then
  159. newmaster = id
  160. break
  161. end
  162. end
  163. self:_set_master(newmaster)
  164. self:_attach_all()
  165. elseif master and ent then
  166. ent:set_detach()
  167. end
  168. if ent then
  169. ent:remove()
  170. end
  171. end,
  172. _add_loaded = function(self)
  173. for id, _ in pairs(self._attached_entities) do
  174. self:_add_attached(id)
  175. end
  176. end,
  177. get_id = function(self)
  178. return self._id
  179. end,
  180. get_pos = function(self)
  181. return vector.new(self._pos)
  182. end,
  183. set_pos = function(self, pos)
  184. self._pos = vector.new(pos)
  185. --for _, entity in pairs(self._attached_entities) do
  186. -- if entity.entity then
  187. -- entity.entity:set_pos(vector.add(self._pos, entity.offset))
  188. -- end
  189. --end
  190. local master = self._attached_entities_master
  191. if master then
  192. local master_def = self._attached_entities[master]
  193. master_def.entity:set_pos(vector.add(self._pos, master_def.offset))
  194. end
  195. end,
  196. get_velocity = function(self)
  197. return vector.new(self._velocity)
  198. end,
  199. set_velocity = function(self, velocity)
  200. self._velocity = vector.new(velocity)
  201. local master = self._attached_entities_master
  202. if master then
  203. self._attached_entities[master].entity:set_velocity(self._velocity)
  204. end
  205. end,
  206. get_acceleration = function(self)
  207. return vector.new(self._acceleration)
  208. end,
  209. set_acceleration = function(self, acceleration)
  210. self._acceleration = vector.new(acceleration)
  211. local master = self._attached_entities_master
  212. if master then
  213. self._attached_entities[master].entity:set_acceleration(self._acceleration)
  214. end
  215. end,
  216. remove = function(self)
  217. self:_detach_all()
  218. for _, entity in pairs(self._attached_entities) do
  219. if entity.entity then
  220. entity.entity:remove()
  221. end
  222. end
  223. luaentity.entities[self._id] = nil
  224. end,
  225. add_attached_entity = function(self, name, data, offset)
  226. local index = #self._attached_entities + 1
  227. self._attached_entities[index] = {
  228. name = name,
  229. data = data,
  230. offset = vector.new(offset),
  231. }
  232. self:_add_attached(index)
  233. return index
  234. end,
  235. remove_attached_entity = function(self, index)
  236. self:_remove_attached(index)
  237. self._attached_entities[index] = nil
  238. end,
  239. }
  240. function luaentity.register_entity(name, prototype)
  241. -- name = check_modname_prefix(name)
  242. prototype.name = name
  243. setmetatable(prototype, {__index = entitydef_default})
  244. prototype.__index = prototype -- Make it possible to use it as metatable
  245. luaentity.registered_entities[name] = prototype
  246. end
  247. -- function luaentity.get_entity_definition(entity)
  248. -- return luaentity.registered_entities[entity.name]
  249. -- end
  250. function luaentity.add_entity(pos, name)
  251. if not luaentity.entities then
  252. minetest.after(0, luaentity.add_entity, vector.new(pos), name)
  253. return
  254. end
  255. local index = luaentity.entities_index
  256. while luaentity.entities[index] do
  257. index = index + 1
  258. if index >= max_entity_id then
  259. index = 0
  260. end
  261. end
  262. luaentity.entities_index = index
  263. local entity = {
  264. name = name,
  265. _id = index,
  266. _pos = vector.new(pos),
  267. _velocity = {x = 0, y = 0, z = 0},
  268. _acceleration = {x = 0, y = 0, z = 0},
  269. _attached_entities = {},
  270. }
  271. local prototype = luaentity.registered_entities[name]
  272. setmetatable(entity, prototype) -- Default to prototype for other methods
  273. luaentity.entities[index] = entity
  274. if entity.on_activate then
  275. entity:on_activate()
  276. end
  277. return entity
  278. end
  279. -- todo: check if remove in get_staticdata works
  280. function luaentity.get_staticdata(self)
  281. local parent = luaentity.entities[self.parent_id]
  282. if parent and parent._remove_attached then
  283. parent:_remove_attached(self.attached_id)
  284. end
  285. return "toremove"
  286. end
  287. function luaentity.on_activate(self, staticdata)
  288. if staticdata == "toremove" then
  289. self.object:remove()
  290. end
  291. end
  292. function luaentity.get_objects_inside_radius(pos, radius)
  293. local objects = {}
  294. local index = 1
  295. for id, entity in pairs(luaentity.entities) do
  296. if vector.distance(pos, entity:get_pos()) <= radius then
  297. objects[index] = entity
  298. index = index + 1
  299. end
  300. end
  301. end
  302. local move_entities_globalstep_part2 = function(dtime)
  303. if not luaentity.entities then
  304. luaentity.entities = read_entities()
  305. end
  306. for id, entity in pairs(luaentity.entities) do
  307. local master = entity._attached_entities_master
  308. local master_def = master and entity._attached_entities[master]
  309. local master_entity = master_def and master_def.entity
  310. local master_entity_pos = master_entity and master_entity:get_pos()
  311. if master_entity_pos then
  312. entity._pos = vector.subtract(master_entity_pos, master_def.offset)
  313. entity._velocity = master_entity:get_velocity()
  314. entity._acceleration = master_entity:get_acceleration()
  315. else
  316. entity._pos = vector.add(vector.add(
  317. entity._pos,
  318. vector.multiply(entity._velocity, dtime)),
  319. vector.multiply(entity._acceleration, 0.5 * dtime * dtime))
  320. entity._velocity = vector.add(
  321. entity._velocity,
  322. vector.multiply(entity._acceleration, dtime))
  323. end
  324. if master and not master_entity_pos then -- The entity has somehow been cleared
  325. if pipeworks.delete_item_on_clearobject then
  326. entity:remove()
  327. else
  328. entity:_remove_attached(master)
  329. entity:_add_loaded()
  330. if entity.on_step then
  331. entity:on_step(dtime)
  332. end
  333. end
  334. else
  335. entity:_add_loaded()
  336. if entity.on_step then
  337. entity:on_step(dtime)
  338. end
  339. end
  340. end
  341. end
  342. local handle_active_blocks_timer = 0.1
  343. minetest.register_globalstep(function(dtime)
  344. handle_active_blocks_timer = handle_active_blocks_timer + dtime
  345. if dtime < 0.2 or handle_active_blocks_timer >= (dtime * 3) then
  346. handle_active_blocks_timer = 0.1
  347. move_entities_globalstep_part1(dtime)
  348. move_entities_globalstep_part2(dtime)
  349. end
  350. end)