123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380 |
- 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 id, 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 id, 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._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)
|