local max_entity_id = 1000000000000 -- If you need more, there's a problem with your code local luaentity = {} pipeworks.luaentity = luaentity luaentity.registered_entities = {} local filename = minetest.get_worldpath().."/luaentities" local function read_file() local f = io.open(filename, "r") if f == nil then return {} end local t = f:read("*all") f:close() if t == "" or t == nil then return {} end return minetest.deserialize(t) or {} end local function write_file(tbl) local f = io.open(filename, "w") f:write(minetest.serialize(tbl)) f:close() end local function read_entities() local t = read_file() for _, entity in pairs(t) do local x=entity.start_pos.x local y=entity.start_pos.y local z=entity.start_pos.z x=math.max(-30912,x) y=math.max(-30912,y) z=math.max(-30912,z) x=math.min(30927,x) y=math.min(30927,y) z=math.min(30927,z) entity.start_pos.x = x entity.start_pos.y = y entity.start_pos.z = z setmetatable(entity, luaentity.registered_entities[entity.name]) end return t end local function write_entities() if not luaentity.entities then -- This can happen if crashing on startup, causing another error that -- masks the original one. Return gracefully in that case instead. return end for _, entity in pairs(luaentity.entities) do setmetatable(entity, nil) for _, attached in pairs(entity._attached_entities) do if attached.entity then attached.entity:remove() attached.entity = nil end end entity._attached_entities_master = nil end write_file(luaentity.entities) end minetest.register_on_shutdown(write_entities) luaentity.entities_index = 0 local function get_blockpos(pos) return {x = math.floor(pos.x / 16), y = math.floor(pos.y / 16), z = math.floor(pos.z / 16)} end local active_blocks = {} -- These only contain active blocks near players (i.e., not forceloaded ones) local move_entities_globalstep_part1 = function(dtime) local active_block_range = tonumber(minetest.settings:get("active_block_range")) or 2 local new_active_blocks = {} for _, player in ipairs(minetest.get_connected_players()) do local blockpos = get_blockpos(player:get_pos()) local minp = vector.subtract(blockpos, active_block_range) local maxp = vector.add(blockpos, active_block_range) for x = minp.x, maxp.x do for y = minp.y, maxp.y do for z = minp.z, maxp.z do local pos = {x = x, y = y, z = z} new_active_blocks[minetest.hash_node_position(pos)] = pos end end end end active_blocks = new_active_blocks -- todo: callbacks on block load/unload end local function is_active(pos) return active_blocks[minetest.hash_node_position(get_blockpos(pos))] ~= nil end local entitydef_default = { _attach = function(self, attached, attach_to) local attached_def = self._attached_entities[attached] local attach_to_def = self._attached_entities[attach_to] attached_def.entity:set_attach( attach_to_def.entity, "", vector.subtract(attached_def.offset, attach_to_def.offset), -- todo: Does not work because is object space vector.new(0, 0, 0) ) end, _set_master = function(self, index) self._attached_entities_master = index if not index then return end local def = self._attached_entities[index] if not def.entity then return end def.entity:set_pos(vector.add(self._pos, def.offset)) def.entity:set_velocity(self._velocity) def.entity:set_acceleration(self._acceleration) end, _attach_all = function(self) local master = self._attached_entities_master if not master then return end for id, entity in pairs(self._attached_entities) do if id ~= master and entity.entity then self:_attach(id, master) end end end, _detach_all = function(self) local master = self._attached_entities_master for id, entity in pairs(self._attached_entities) do if id ~= master and entity.entity then entity.entity:set_detach() end end end, _add_attached = function(self, index) local entity = self._attached_entities[index] if entity.entity then return end local entity_pos = vector.add(self._pos, entity.offset) if not is_active(entity_pos) then return end local ent = minetest.add_entity(entity_pos, entity.name):get_luaentity() ent:from_data(entity.data) ent.parent_id = self._id ent.attached_id = index entity.entity = ent.object local master = self._attached_entities_master if master then self:_attach(index, master) else self:_set_master(index) end end, _remove_attached = function(self, index) local master = self._attached_entities_master local entity = self._attached_entities[index] local ent = entity and entity.entity if entity then entity.entity = nil end if index == master then self:_detach_all() local newmaster for id, attached in pairs(self._attached_entities) do if id ~= master and attached.entity then newmaster = id break end end self:_set_master(newmaster) self:_attach_all() elseif master and ent then ent:set_detach() end if ent then ent:remove() end end, _add_loaded = function(self) for id, _ in pairs(self._attached_entities) do self:_add_attached(id) end end, get_id = function(self) return self._id end, get_pos = function(self) return vector.new(self._pos) end, set_pos = function(self, pos) self._pos = vector.new(pos) --for _, entity in pairs(self._attached_entities) do -- if entity.entity then -- entity.entity:set_pos(vector.add(self._pos, entity.offset)) -- end --end local master = self._attached_entities_master if master then local master_def = self._attached_entities[master] master_def.entity:set_pos(vector.add(self._pos, master_def.offset)) end end, get_velocity = function(self) return vector.new(self._velocity) end, set_velocity = function(self, velocity) self._velocity = vector.new(velocity) local master = self._attached_entities_master if master then self._attached_entities[master].entity:set_velocity(self._velocity) end end, get_acceleration = function(self) return vector.new(self._acceleration) end, set_acceleration = function(self, acceleration) self._acceleration = vector.new(acceleration) local master = self._attached_entities_master if master then self._attached_entities[master].entity:set_acceleration(self._acceleration) end end, remove = function(self) self:_detach_all() for _, entity in pairs(self._attached_entities) do if entity.entity then entity.entity:remove() end end luaentity.entities[self._id] = nil end, add_attached_entity = function(self, name, data, offset) local index = #self._attached_entities + 1 self._attached_entities[index] = { name = name, data = data, offset = vector.new(offset), } self:_add_attached(index) return index end, remove_attached_entity = function(self, index) self:_remove_attached(index) self._attached_entities[index] = nil end, } function luaentity.register_entity(name, prototype) -- name = check_modname_prefix(name) prototype.name = name setmetatable(prototype, {__index = entitydef_default}) prototype.__index = prototype -- Make it possible to use it as metatable luaentity.registered_entities[name] = prototype end -- function luaentity.get_entity_definition(entity) -- return luaentity.registered_entities[entity.name] -- end function luaentity.add_entity(pos, name) if not luaentity.entities then minetest.after(0, luaentity.add_entity, vector.new(pos), name) return end local index = luaentity.entities_index while luaentity.entities[index] do index = index + 1 if index >= max_entity_id then index = 0 end end luaentity.entities_index = index local entity = { name = name, _id = index, _pos = vector.new(pos), _velocity = {x = 0, y = 0, z = 0}, _acceleration = {x = 0, y = 0, z = 0}, _attached_entities = {}, } local prototype = luaentity.registered_entities[name] setmetatable(entity, prototype) -- Default to prototype for other methods luaentity.entities[index] = entity if entity.on_activate then entity:on_activate() end return entity end -- todo: check if remove in get_staticdata works function luaentity.get_staticdata(self) local parent = luaentity.entities[self.parent_id] if parent and parent._remove_attached then parent:_remove_attached(self.attached_id) end return "toremove" end function luaentity.on_activate(self, staticdata) if staticdata == "toremove" then self.object:remove() end end function luaentity.get_objects_inside_radius(pos, radius) local objects = {} local index = 1 for _, entity in pairs(luaentity.entities) do if vector.distance(pos, entity:get_pos()) <= radius then objects[index] = entity index = index + 1 end end end local move_entities_globalstep_part2 = function(dtime) if not luaentity.entities then luaentity.entities = read_entities() end for _, entity in pairs(luaentity.entities) do local master = entity._attached_entities_master local master_def = master and entity._attached_entities[master] local master_entity = master_def and master_def.entity local master_entity_pos = master_entity and master_entity:get_pos() if master_entity_pos then entity._pos = vector.subtract(master_entity_pos, master_def.offset) entity._velocity = master_entity:get_velocity() entity._acceleration = master_entity:get_acceleration() else entity._velocity = entity._velocity or vector.new(0,0,0) entity._acceleration = entity._acceleration or vector.new(0,0,0) entity._pos = vector.add(vector.add( entity._pos, vector.multiply(entity._velocity, dtime)), vector.multiply(entity._acceleration, 0.5 * dtime * dtime)) entity._velocity = vector.add( entity._velocity, vector.multiply(entity._acceleration, dtime)) end if master and not master_entity_pos then -- The entity has somehow been cleared if pipeworks.delete_item_on_clearobject then entity:remove() else entity:_remove_attached(master) entity:_add_loaded() if entity.on_step then entity:on_step(dtime) end end else entity:_add_loaded() if entity.on_step then entity:on_step(dtime) end end end end local handle_active_blocks_timer = 0.1 minetest.register_globalstep(function(dtime) handle_active_blocks_timer = handle_active_blocks_timer + dtime if dtime < 0.2 or handle_active_blocks_timer >= (dtime * 3) then handle_active_blocks_timer = 0.1 move_entities_globalstep_part1(dtime) move_entities_globalstep_part2(dtime) end end)