From 60fb79215104dbc984ca26234f7216424114a952 Mon Sep 17 00:00:00 2001 From: sapier Date: Mon, 9 Dec 2013 15:25:55 +0100 Subject: [PATCH] Add support for offline mob accounting Fix bug orphaned lifebars stay forever Fix crash due to invalid data passed to path based movegen --- mobf/init.lua | 3 + mobf/lifebar.lua | 8 +- mobf/mobf.lua | 18 +++- mobf/spawning.lua | 149 ++++++++++++++++++++++++++++++- mobf/utils/generic_functions.lua | 24 +++++ 5 files changed, 195 insertions(+), 7 deletions(-) diff --git a/mobf/init.lua b/mobf/init.lua index 88d5e18..7fab4dd 100644 --- a/mobf/init.lua +++ b/mobf/init.lua @@ -196,6 +196,9 @@ function mobf_init_framework() minetest.log(LOGLEVEL_NOTICE,"MOBF: Initialize lifebar subsystem..") mobf_lifebar.init() + minetest.log(LOGLEVEL_NOTICE,"MOBF: Initialize spawning subsystem..") + spawning.init() + minetest.log(LOGLEVEL_NOTICE,"MOBF: Initialize mobf supplied modules..") mobf_init_modules() diff --git a/mobf/lifebar.lua b/mobf/lifebar.lua index 9a33010..edee911 100644 --- a/mobf/lifebar.lua +++ b/mobf/lifebar.lua @@ -46,14 +46,18 @@ function mobf_lifebar.init() initialized = false, on_step = function (self,dtime) + self.lifetime = self.lifetime + dtime if not self.initialized then - self.lifetime = self.lifetime + dtime - if self.lifetime > 1 then dbg_mobf.lifebar_lvl3("MOBF: lifebar not attached deleting") self.object:remove() end end + + --parent will reset lifetime while it's active + if self.lifetime > 5 then + self.object:remove() + end end }) diff --git a/mobf/mobf.lua b/mobf/mobf.lua index fac2391..926f96e 100644 --- a/mobf/mobf.lua +++ b/mobf/mobf.lua @@ -467,7 +467,7 @@ function mobf.activate_handler(self,staticdata) mobf_assert_backtrace(self.dynamic_data.current_movement_gen ~= nil) --initialize movegen entity,current time, permanent data - self.dynamic_data.current_movement_gen.init_dynamic_data(self,now,retval) + self.dynamic_data.current_movement_gen.init_dynamic_data(self,now,preserved_data) --call enter state fct if self.dynamic_data.state.current.HANDLER_enter_state ~= nil then @@ -632,6 +632,10 @@ function mobf.register_entity(name, graphics, mob) return end + if self.lifebar ~= nil then + self.lifebar:get_luaentity().lifetime = 0 + end + --check lifetime if spawning.lifecycle_callback(self,now) == false then mobf_warn_long_fct(starttime,"on_step_total_lifecycle","on_step_total") @@ -703,6 +707,9 @@ function mobf.register_entity(name, graphics, mob) --make sure entity is in loaded area at initialization local pos = self.object:getpos() + --remove from mob offline storage + spawning.activate_mob(self.data.modname .. ":" .. self.data.name,pos) + if pos ~= nil and entity_at_loaded_pos(pos,self.data.name) then mobf.activate_handler(self,staticdata) @@ -741,9 +748,12 @@ function mobf.register_entity(name, graphics, mob) end, --prepare permanent data - get_staticdata = function(self) - return mobf_serialize_permanent_entity_data(self) - end, + --NOTE this isn't called if a object is deleted + get_staticdata = function(self) + --add to mob offline storage + spawning.deactivate_mob(self.data.modname .. ":" .. self.data.name,self.object:getpos()) + return mobf_serialize_permanent_entity_data(self) + end, --custom variables for each mob data = mob, diff --git a/mobf/spawning.lua b/mobf/spawning.lua index 9d8cf19..694d790 100644 --- a/mobf/spawning.lua +++ b/mobf/spawning.lua @@ -35,6 +35,153 @@ mobf_assert_backtrace(mobf_spawn_algorithms == nil) --! @private mobf_spawn_algorithms = {} +------------------------------------------------------------------------------- +-- name: init() +-- @function [parent=#spawning] init +-- +--! @brief initialize spawning data +--! @memberof spawning +-- +------------------------------------------------------------------------------- +function spawning.init() + --read from file + local world_path = minetest.get_worldpath() + + local file,error = io.open(world_path .. "/mobf_spawning_data","r") + + if file ~= nil then + local data_raw = file:read("*a") + file:close() + + if data_raw ~= nil then + spawning.mob_spawn_data = minetest.deserialize(data_raw) + end + end + + if spawning.mob_spawn_data == nil then + spawning.mob_spawn_data = {} + end + + --register spawndata persistent storer to globalstep + minetest.after(300,spawning.preserve_spawn_data,true) + + --register cleanup handler + minetest.register_on_shutdown(function(dstep) spawning.preserve_spawn_data(false) end) +end + +------------------------------------------------------------------------------- +-- name: preserve_spawn_data() +-- @function [parent=#spawning] preserve_spawn_data +-- +--! @brief save data on regular base +--! @memberof spawning +-- +--! @param force +------------------------------------------------------------------------------- +function spawning.preserve_spawn_data(cyclic) + + local world_path = minetest.get_worldpath() + local file,error = io.open(world_path .. "/mobf_spawning_data","w") + + if error ~= nil then + minetest.log(LOGLEVEL_ERROR,"MOBF: failed to spawning preserve file") + end + mobf_assert_backtrace(file ~= nil) + + local serialized_data = minetest.serialize(spawning.mob_spawn_data) + + file:write(serialized_data) + + if cyclic then + minetest.after(300,spawning.preserve_spawn_data,cyclic) + end +end + +------------------------------------------------------------------------------- +-- name: total_offline_mobs() +-- @function [parent=#spawning] total_offline_mobs +-- +--! @brief count total number of offline mobs +--! @memberof spawning +-- +--! @return number of mobs +------------------------------------------------------------------------------- +function spawning.total_offline_mobs() + local count = 0 + for key,value in pairs(spawning.mob_spawn_data) do + for hash,v in pairs(value) do + count = count +1 + end + end + + return count +end + +------------------------------------------------------------------------------- +-- name: count_deactivated_mobs(name,pos,range) +-- @function [parent=#spawning] count_deactivated_mobs +-- +--! @brief count number of mobs of specific type within a certain range +--! @memberof spawning +-- +--! @param name name of mob to count +--! @param pos to check distance to +--! @param range to check +-- +--! @return number of mobs +------------------------------------------------------------------------------- +function spawning.count_deactivated_mobs(name,pos,range) + local count = 0 + if spawning.mob_spawn_data[name] ~= nil then + for hash,v in pairs(spawning.mob_spawn_data[name]) do + local mobpos = mobf_hash_to_pos(hash) + local distance = vector.distance(pos,mobpos) + if distance < range then + count = count +1 + end + end + end + return count +end + +------------------------------------------------------------------------------- +-- name: deactivate_mob(entity) +-- @function [parent=#spawning] deactivate_mob +-- +--! @brief add mob to deactivated list +--! @memberof spawning +-- +--! @param entity to deactivate +------------------------------------------------------------------------------- +function spawning.deactivate_mob(name,pos) + if spawning.mob_spawn_data[name] == nil then + spawning.mob_spawn_data[name] = {} + end + + local rounded_pos = vector.round(pos) + local hash = minetest.hash_node_position(rounded_pos) + --assert (mobf_pos_is_same(mobf_hash_to_pos(hash),rounded_pos)) + spawning.mob_spawn_data[name][hash] = true +end + +------------------------------------------------------------------------------- +-- name: activate_mob(name,pos) +-- @function [parent=#spawning] preserve_spawn_data +-- +--! @brief save data on regular base +--! @memberof spawning +-- +--! @param force +------------------------------------------------------------------------------- +function spawning.activate_mob(name,pos) + if spawning.mob_spawn_data[name] ~= nil then + local rounded_pos = vector.round(pos) + local hash = minetest.hash_node_position(rounded_pos) + --assert(mobf_pos_is_same(mobf_hash_to_pos(hash),rounded_pos)) + spawning.mob_spawn_data[name][hash] = nil + end +end + ------------------------------------------------------------------------------- -- name: remove_uninitialized(entity,staticdata) @@ -198,7 +345,7 @@ function spawning.population_density_check(entity,now) mob_count .. " mobs of same type around") entity.removed = true minetest.log(LOGLEVEL_WARNING,"MOBF: Too many ".. mob_count .. " ".. - entity.data.name.." at one place dying: " .. + entity.data.name.." at one place dieing: " .. tostring(entity.dynamic_data.spawning.player_spawned)) spawning.remove(entity, "population density check") return false diff --git a/mobf/utils/generic_functions.lua b/mobf/utils/generic_functions.lua index a959409..009a68f 100644 --- a/mobf/utils/generic_functions.lua +++ b/mobf/utils/generic_functions.lua @@ -733,4 +733,28 @@ function mobf_is_pos(value) return true end +------------------------------------------------------------------------------- +-- name: mobf_hash_to_pos(hash) +-- +--! @brief restore a position from a pos hash value +-- +--! @param hash to restore pos from +-- +--! @return posistion reconstructed from hash +------------------------------------------------------------------------------- +function mobf_hash_to_pos(hash) + local retval = {} + + local raw_x = (hash % 65536) + local raw_y = ((hash - raw_x) % (65536*65536)) / 65536 + local raw_z = ((hash - raw_x - raw_y) / 65536) / 65536 + + local mobpos = {} + retval.x = raw_x - 32768 + retval.y = raw_y - 32768 + retval.z = math.floor(raw_z - 32768) + + return retval +end + --!@}