New timer design.
I could honestly not make much sense of the timer implementation that was here. Instead I've implemented the type of timer algorithm that I've used before, and tested it instead. The concept is extremely simple: all timers are put in an ordered list. We check every server tick if any of the timers have elapsed, and execute the function associated with this timer. We know that many timers by themselves cause new timers to be added to this list, so we iterate *backwards* over the timer list. This means that new timers being added while timers are being executed, can never be executed in the same function pass, as they are always appended to the table *after* the end of the table, which we will never reach in the current pass over all the table elements. We switch time keeping to minetest.get_us_time(). dtime is likely unreliable and we have our own high-res timer that we can fix if it is indeed broken. This removes the need to do any sort of time keeping.master
parent
4ac1e9bccb
commit
ad884f23d4
|
@ -4,74 +4,48 @@
|
||||||
-- Misc. API functions
|
-- Misc. API functions
|
||||||
--
|
--
|
||||||
|
|
||||||
local timers = {}
|
local jobs = {}
|
||||||
local mintime
|
local time = 0.0
|
||||||
local function update_timers(delay)
|
local last = 0.0
|
||||||
mintime = false
|
|
||||||
local sub = 0
|
core.register_globalstep(function(dtime)
|
||||||
for index = 1, #timers do
|
local new = core.get_us_time() / 1000000
|
||||||
index = index - sub
|
if new > last then
|
||||||
local timer = timers[index]
|
time = time + (new - last)
|
||||||
timer.time = timer.time - delay
|
else
|
||||||
if timer.time <= 0 then
|
-- Overflow, we may lose a little bit of time here but
|
||||||
core.set_last_run_mod(timer.mod_origin)
|
-- only 1 tick max, potentially running timers slightly
|
||||||
timer.func(unpack(timer.args or {}))
|
-- too early.
|
||||||
table.remove(timers, index)
|
time = time + new
|
||||||
sub = sub + 1
|
end
|
||||||
elseif mintime then
|
last = new
|
||||||
mintime = math.min(mintime, timer.time)
|
|
||||||
else
|
if #jobs < 1 then
|
||||||
mintime = timer.time
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Iterate backwards so that we miss any new timers added by
|
||||||
|
-- a timer callback, and so that we don't skip the next timer
|
||||||
|
-- in the list if we remove one.
|
||||||
|
for i = #jobs, 1, -1 do
|
||||||
|
local job = jobs[i]
|
||||||
|
if time >= job.expire then
|
||||||
|
core.set_last_run_mod(job.mod_origin)
|
||||||
|
job.func(unpack(job.arg))
|
||||||
|
table.remove(jobs, i)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
local timers_to_add
|
|
||||||
local function add_timers()
|
|
||||||
for _, timer in ipairs(timers_to_add) do
|
|
||||||
table.insert(timers, timer)
|
|
||||||
end
|
|
||||||
timers_to_add = false
|
|
||||||
end
|
|
||||||
|
|
||||||
local delay = 0
|
|
||||||
core.register_globalstep(function(dtime)
|
|
||||||
if not mintime then
|
|
||||||
-- abort if no timers are running
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if timers_to_add then
|
|
||||||
add_timers()
|
|
||||||
end
|
|
||||||
delay = delay + dtime
|
|
||||||
if delay < mintime then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
update_timers(delay)
|
|
||||||
delay = 0
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
function core.after(time, func, ...)
|
function core.after(after, func, ...)
|
||||||
assert(tonumber(time) and type(func) == "function",
|
assert(tonumber(time) and type(func) == "function",
|
||||||
"Invalid core.after invocation")
|
"Invalid core.after invocation")
|
||||||
if not mintime then
|
table.insert(jobs, {
|
||||||
mintime = time
|
func = func,
|
||||||
timers_to_add = {{
|
expire = time + after,
|
||||||
time = time+delay,
|
arg = {...},
|
||||||
func = func,
|
mod_origin = core.get_last_run_mod()
|
||||||
args = {...},
|
})
|
||||||
mod_origin = core.get_last_run_mod(),
|
|
||||||
}}
|
|
||||||
return
|
|
||||||
end
|
|
||||||
mintime = math.min(mintime, time)
|
|
||||||
timers_to_add = timers_to_add or {}
|
|
||||||
timers_to_add[#timers_to_add+1] = {
|
|
||||||
time = time+delay,
|
|
||||||
func = func,
|
|
||||||
args = {...},
|
|
||||||
mod_origin = core.get_last_run_mod(),
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function core.check_player_privs(player_or_name, ...)
|
function core.check_player_privs(player_or_name, ...)
|
||||||
|
|
|
@ -2242,7 +2242,7 @@ These functions return the leftover itemstack.
|
||||||
|
|
||||||
### Timing
|
### Timing
|
||||||
* `minetest.after(time, func, ...)`
|
* `minetest.after(time, func, ...)`
|
||||||
* Call the function `func` after `time` seconds
|
* Call the function `func` after `time` seconds, may be fractional
|
||||||
* Optional: Variable number of arguments that are passed to `func`
|
* Optional: Variable number of arguments that are passed to `func`
|
||||||
|
|
||||||
### Server
|
### Server
|
||||||
|
|
Loading…
Reference in New Issue