238 lines
4.8 KiB
Lua
238 lines
4.8 KiB
Lua
local time = require 'bee.time'
|
|
local setmetatable = setmetatable
|
|
local mathMax = math.max
|
|
local mathFloor = math.floor
|
|
local monotonic = time.monotonic
|
|
local xpcall = xpcall
|
|
local logError = log.error
|
|
|
|
_ENV = nil
|
|
|
|
local curFrame = 0
|
|
local maxFrame = 0
|
|
local curIndex = 0
|
|
local tarFrame = 0
|
|
local fwFrame = 0
|
|
local freeQueue = {}
|
|
local timer = {}
|
|
|
|
local function allocQueue()
|
|
local n = #freeQueue
|
|
if n > 0 then
|
|
local r = freeQueue[n]
|
|
freeQueue[n] = nil
|
|
return r
|
|
else
|
|
return {}
|
|
end
|
|
end
|
|
|
|
local function mTimeout(self, timeout)
|
|
if self._pauseRemaining or self._running then
|
|
return
|
|
end
|
|
local ti = tarFrame + timeout
|
|
local q = timer[ti]
|
|
if q == nil then
|
|
q = allocQueue()
|
|
timer[ti] = q
|
|
end
|
|
self._timeoutFrame = ti
|
|
self._running = true
|
|
q[#q + 1] = self
|
|
end
|
|
|
|
local function mWakeup(self)
|
|
if self._removed then
|
|
return
|
|
end
|
|
self._running = false
|
|
if self._onTimer then
|
|
xpcall(self._onTimer, logError, self)
|
|
end
|
|
if self._removed then
|
|
return
|
|
end
|
|
if self._timerCount then
|
|
if self._timerCount > 1 then
|
|
self._timerCount = self._timerCount - 1
|
|
mTimeout(self, self._timeout)
|
|
else
|
|
self._removed = true
|
|
end
|
|
else
|
|
mTimeout(self, self._timeout)
|
|
end
|
|
end
|
|
|
|
local function getRemaining(self)
|
|
if self._removed then
|
|
return 0
|
|
end
|
|
if self._pauseRemaining then
|
|
return self._pauseRemaining
|
|
end
|
|
if self._timeoutFrame == curFrame then
|
|
return self._timeout or 0
|
|
end
|
|
return self._timeoutFrame - curFrame
|
|
end
|
|
|
|
local function onTick()
|
|
local q = timer[curFrame]
|
|
if q == nil then
|
|
curIndex = 0
|
|
return
|
|
end
|
|
for i = curIndex + 1, #q do
|
|
local callback = q[i]
|
|
curIndex = i
|
|
q[i] = nil
|
|
if callback then
|
|
mWakeup(callback)
|
|
end
|
|
end
|
|
curIndex = 0
|
|
timer[curFrame] = nil
|
|
freeQueue[#freeQueue + 1] = q
|
|
end
|
|
|
|
---@class timer.manager
|
|
local m = {}
|
|
|
|
---@class timer
|
|
---@field _onTimer? fun(self: timer)
|
|
---@field _timeoutFrame integer
|
|
---@field _timeout integer
|
|
local mt = {}
|
|
mt.__index = mt
|
|
mt.type = 'timer'
|
|
|
|
function mt:__tostring()
|
|
return '[table:timer]'
|
|
end
|
|
|
|
function mt:__call()
|
|
if self._onTimer then
|
|
self:_onTimer()
|
|
end
|
|
end
|
|
|
|
function mt:remove()
|
|
self._removed = true
|
|
end
|
|
|
|
function mt:pause()
|
|
if self._removed or self._pauseRemaining then
|
|
return
|
|
end
|
|
self._pauseRemaining = getRemaining(self)
|
|
self._running = false
|
|
local ti = self._timeoutFrame
|
|
local q = timer[ti]
|
|
if q then
|
|
for i = #q, 1, -1 do
|
|
if q[i] == self then
|
|
q[i] = false
|
|
return
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function mt:resume()
|
|
if self._removed or not self._pauseRemaining then
|
|
return
|
|
end
|
|
local timeout = self._pauseRemaining
|
|
self._pauseRemaining = nil
|
|
mTimeout(self, timeout)
|
|
end
|
|
|
|
function mt:restart()
|
|
if self._removed or self._pauseRemaining or not self._running then
|
|
return
|
|
end
|
|
local ti = self._timeoutFrame
|
|
local q = timer[ti]
|
|
if q then
|
|
for i = #q, 1, -1 do
|
|
if q[i] == self then
|
|
q[i] = false
|
|
break
|
|
end
|
|
end
|
|
end
|
|
self._running = false
|
|
mTimeout(self, self._timeout)
|
|
end
|
|
|
|
function mt:remaining()
|
|
return getRemaining(self) / 1000.0
|
|
end
|
|
|
|
function mt:onTimer()
|
|
self:_onTimer()
|
|
end
|
|
|
|
function m.wait(timeout, onTimer)
|
|
local _timeout = mathMax(mathFloor(timeout * 1000.0), 1)
|
|
local t = setmetatable({
|
|
['_timeout'] = _timeout,
|
|
['_onTimer'] = onTimer,
|
|
['_timerCount'] = 1,
|
|
}, mt)
|
|
mTimeout(t, _timeout)
|
|
return t
|
|
end
|
|
|
|
function m.loop(timeout, onTimer)
|
|
local _timeout = mathFloor(timeout * 1000.0)
|
|
local t = setmetatable({
|
|
['_timeout'] = _timeout,
|
|
['_onTimer'] = onTimer,
|
|
}, mt)
|
|
mTimeout(t, _timeout)
|
|
return t
|
|
end
|
|
|
|
function m.timer(timeout, count, onTimer)
|
|
if count == 0 then
|
|
return m.loop(timeout, onTimer)
|
|
end
|
|
local _timeout = mathFloor(timeout * 1000.0)
|
|
local t = setmetatable({
|
|
['_timeout'] = _timeout,
|
|
['_onTimer'] = onTimer,
|
|
['_timerCount'] = count,
|
|
}, mt)
|
|
mTimeout(t, _timeout)
|
|
return t
|
|
end
|
|
|
|
function m.clock()
|
|
return curFrame / 1000.0
|
|
end
|
|
|
|
local lastClock = monotonic()
|
|
function m.update()
|
|
local currentClock = monotonic() + fwFrame
|
|
local delta = currentClock - lastClock
|
|
lastClock = currentClock
|
|
if curIndex ~= 0 then
|
|
curFrame = curFrame - 1
|
|
end
|
|
maxFrame = maxFrame + delta
|
|
tarFrame = mathFloor(maxFrame)
|
|
while curFrame < maxFrame do
|
|
curFrame = curFrame + 1
|
|
onTick()
|
|
end
|
|
end
|
|
|
|
function m.timeJump(delta)
|
|
fwFrame = fwFrame + mathFloor(delta * 1000.0)
|
|
end
|
|
|
|
return m
|