local dbg if moddebug then dbg=moddebug.dbg("people") else dbg={v1=function() end,v2=function() end,v3=function() end} end -- This table tracks all the people in the world. It's keyed on the person -- name, and each entry is a table keyed as follows: -- entity - the active luaentity reference, or nil if not active -- pos - when inactive, the entity's position -- waketime - time in seconds until this entity should be re-awoken, -- or nil (only ever set when inactive) -- waking - used internally to track wakeup status -- dead - used internally to track when dead -- wakecmds - list of commands to run on activation, each being -- at table of {name, args} -- Changes to this should be via the functions below. people.people = {} local file = io.open(minetest.get_worldpath().."/people_people", "r") if file then people.people = minetest.deserialize(file:read("*all")) file:close() end local people_save = function() -- We save without the entity field, because all will be inactive -- when reloaded local speople = {} for k, v in pairs(people.people) do speople[k] = { pos = v.pos, waketime = v.waketime, } end local file = io.open(minetest.get_worldpath().."/people_people", "w") if file then file:write(minetest.serialize(speople)) file:close() end end people.people_add = function(entity) people.people[entity.name] = {entity=entity} dbg.v2(entity.name.." added") end people.people_set_active = function(entity) local pr = people.people[entity.name] if not pr then -- Should never happen dbg.v1(entity.name.." reactivated without existing record") pr = {} people.people[entity.name] = pr end pr.entity = entity if pr.waketime then local loadpos = pr.pos if not loadpos then dbg.v1("Activated entity has waketime but no pos") else dbg.v2("Free forceload for "..minetest.pos_to_string(loadpos)) minetest.forceload_free_block(loadpos) end pr.pos = nil pr.waketime = nil pr.waking = nil end dbg.v2(entity.name.." activated") if pr.wakecmds then -- We can't do these right now, because we're already within the -- on_activate handler for this entity. minetest.after(0, function() for _, wc in pairs(pr.wakecmds) do local name = wc[1] local param = wc[2] dbg.v1("Re-running chat command after wake-up: "..name.." : "..param or "nil") local success, reply = people.do_command(name, param) if reply then minetest.chat_send_player(name, reply) end end pr.wakecmds = {} end) end end people.people_set_inactive = function(entity) if not people.people[entity.name] then dbg.v1("people_set_inactive for "..entity.name or nil.." with no people record") return end if people.people[entity.name].dead then people.people[entity.name] = nil dbg.v2(entity.name.." deactivated forever") else people.people[entity.name].entity = nil people.people[entity.name].pos = entity.object:getpos() if entity.wait == 0 then people.people[entity.name].waketime = minetest.get_gametime() + 1 else people.people[entity.name].waketime = entity.wait end dbg.v2(entity.name.." deactivated") end people_save() end --- Called at regular intervals -- Handles waking entities that are unloaded and shouldn't be any more. -- Once a second is probably about right, although more frequently wouldn't -- hurt. people.people_wake = function() for k, v in pairs(people.people) do if v.waketime and v.waketime <= minetest.get_gametime() and not v.waking then -- Need to flag that we're doing it, because there will be a -- delay before the block is actually loaded. v.waking = true -- Wake the entity by (yuk) setting a forceload. The activation -- of the entity will cause the forceload to be freed. dbg.v2("Set forceload for "..minetest.pos_to_string(v.pos).." to activate "..k) minetest.forceload_block(v.pos) -- Only one at a time, even though this will introduce some -- inaccuracy as to when they actually wake break end end end