people/tracking.lua

134 lines
4.5 KiB
Lua

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