You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

289 lines
11 KiB

local detailsFramework = _G["DetailsFramework"]
if (not detailsFramework or not DetailsFrameworkCanLoad) then
4 years ago
return
end
local C_Timer = _G.C_Timer
local unpack = table.unpack or _G.unpack
local GetTime = GetTime
local CONST_DEBUG_ENABLED = false
--make a namespace for schedules
detailsFramework.Schedules = detailsFramework.Schedules or {}
detailsFramework.Schedules.AfterCombatSchedules = {
withId = {},
withoutId = {},
}
---@class df_schedule : table
---@field NewTicker fun(time: number, callback: function, ...: any): timer
---@field NewLooper fun(time: number, callback: function, loopAmount: number, loopEndCallback: function?, checkPointCallback: function?, ...: any): timer
---@field NewTimer fun(time: number, callback: function, ...: any): timer
---@field Cancel fun(ticker: timer)
---@field After fun(time: number, callback: function)
---@field SetName fun(object: timer, name: string)
---@field RunNextTick fun(callback: function)
---@field AfterCombat fun(callback:function, id:any, ...: any)
---@field CancelAfterCombat fun(id: any)
---@field CancelAllAfterCombat fun()
---@field IsAfterCombatScheduled fun(id: any): boolean
---@field LazyExecute fun(callback: function, payload: table?, maxIterations: number?, onEndCallback: function?): table
---@field AfterById fun(time: number, callback: function, id: any, ...: any): timer
---@class df_looper : table
---@field payload table
---@field callback function
---@field loopEndCallback function?
---@field checkPointCallback function?
---@field nextCheckPoint number
---@field lastLoop number
---@field currentLoop number
---@field Cancel fun()
---@field IsCancelled fun(): boolean
local eventFrame = CreateFrame("frame")
eventFrame:RegisterEvent("PLAYER_REGEN_ENABLED")
eventFrame:SetScript("OnEvent", function(self, event)
if (event == "PLAYER_REGEN_ENABLED") then
for _, schedule in ipairs(detailsFramework.Schedules.AfterCombatSchedules.withoutId) do
xpcall(schedule.callback, geterrorhandler(), unpack(schedule.payload))
end
for _, schedule in pairs(detailsFramework.Schedules.AfterCombatSchedules.withId) do
xpcall(schedule.callback, geterrorhandler(), unpack(schedule.payload))
end
table.wipe(detailsFramework.Schedules.AfterCombatSchedules.withoutId)
table.wipe(detailsFramework.Schedules.AfterCombatSchedules.withId)
end
end)
local triggerScheduledLoop = function(tickerObject)
if (tickerObject:IsCancelled()) then
return
end
local payload = tickerObject.payload
local callback = tickerObject.callback
--local result, errortext = pcall(callback, unpack(payload))
local runOkay, result = xpcall(callback, geterrorhandler(), unpack(payload))
--if (not result) then
-- detailsFramework:Msg("error on scheduler: ",tickerObject.path , tickerObject.name, errortext)
--end
local checkPointCallback = tickerObject.checkPointCallback
if (checkPointCallback) then
if (GetTime() >= tickerObject.nextCheckPoint) then
local checkPointResult = checkPointCallback(unpack(payload))
if (not checkPointResult) then
tickerObject:Cancel()
if (tickerObject.loopEndCallback) then
tickerObject.loopEndCallback()
end
return
end
tickerObject.nextCheckPoint = GetTime() + 1
end
end
tickerObject.currentLoop = tickerObject.currentLoop + 1
if (tickerObject.currentLoop == tickerObject.lastLoop) then
tickerObject:Cancel()
if (tickerObject.loopEndCallback) then
tickerObject.loopEndCallback()
end
end
return result
end
---start a loop which will tick @loopAmount of times, then call @loopEndCallback if exists
---checkPointCallback will be called every time the loop ticks, if it returns false, the loop will be cancelled
---@param time number
---@param callback function
---@param loopAmount number
---@param loopEndCallback function?
---@param checkPointCallback function?
---@vararg any
---@return df_looper
function detailsFramework.Schedules.NewLooper(time, callback, loopAmount, loopEndCallback, checkPointCallback, ...)
local payload = {...}
---@type df_looper
local newLooper = C_Timer.NewTicker(time, triggerScheduledLoop, loopAmount)
newLooper.payload = payload
newLooper.callback = callback
newLooper.loopEndCallback = loopEndCallback
newLooper.checkPointCallback = checkPointCallback
newLooper.nextCheckPoint = GetTime() + 1
newLooper.lastLoop = loopAmount
newLooper.currentLoop = 1
return newLooper
end
--run a scheduled function with its payload
local triggerScheduledTick = function(tickerObject)
local payload = tickerObject.payload
local callback = tickerObject.callback
local runOkay, result = xpcall(callback, geterrorhandler(), unpack(payload))
--local result, errortext = pcall(callback, unpack(payload))
--if (not result) then
-- detailsFramework:Msg("error on scheduler: ",tickerObject.path , tickerObject.name, errortext)
--end
return result
end
--schedule to repeat a task with an interval of @time, keep ticking until cancelled
function detailsFramework.Schedules.NewTicker(time, callback, ...)
local payload = {...}
local newTicker = C_Timer.NewTicker(time, triggerScheduledTick)
newTicker.payload = payload
newTicker.callback = callback
--debug
newTicker.path = CONST_DEBUG_ENABLED and debugstack() or ""
return newTicker
end
--schedule a function/callback/ to run after 'time' with a payload passed in the varargs
--return an object that can be used to cancel the scheduled task
--difference from Schedules.After is that this function returns an object that can be used to cancel the scheduled task and also pass a payload to the callback
--prompt example: schedule 'function variable name' to run after 'time' amount of seconds with payload 'variable name, variable name...'
--prompt example: run 'function name' after 'time' leaving an object as reference
function detailsFramework.Schedules.NewTimer(time, callback, ...)
local payload = {...}
local newTimer = C_Timer.NewTimer(time, triggerScheduledTick)
newTimer.payload = payload
newTimer.callback = callback
newTimer.expireAt = GetTime() + time
--debug
newTimer.path = CONST_DEBUG_ENABLED and debugstack() or ""
return newTimer
end
--cancel an ongoing ticker or timer, the native call tickerObject:Cancel() also works
---prompt example: cancel schedule 'variable name'
---@param tickerObject timer
function detailsFramework.Schedules.Cancel(tickerObject)
--ignore if there's no ticker object
if (tickerObject) then
return tickerObject:Cancel()
end
end
--schedule a task to be executed when the player leaves combat
function detailsFramework.Schedules.AfterCombat(callback, id, ...)
local bInCombatLockdown = UnitAffectingCombat("player") or InCombatLockdown()
if (not bInCombatLockdown) then
xpcall(callback, geterrorhandler(), ...)
return
end
local payload = {...}
if (id) then
detailsFramework.Schedules.AfterCombatSchedules.withId[id] = {
callback = callback,
payload = payload,
id = id,
}
else
table.insert(detailsFramework.Schedules.AfterCombatSchedules.withoutId, {
callback = callback,
payload = payload,
})
end
end
function detailsFramework.Schedules.CancelAfterCombat(id)
detailsFramework.Schedules.AfterCombatSchedules.withId[id] = nil
end
function detailsFramework.Schedules.CancelAllAfterCombat()
table.wipe(detailsFramework.Schedules.AfterCombatSchedules.withId)
table.wipe(detailsFramework.Schedules.AfterCombatSchedules.withoutId)
end
function detailsFramework.Schedules.IsAfterCombatScheduled(id)
return detailsFramework.Schedules.AfterCombatSchedules.withId[id] ~= nil
end
---execute each frame a small portion of a big task
---the callback function receives a payload, the current iteration index and the max iterations
---if the callback function return true, the task is finished
---@param callback function
---@param payload table?
---@param maxIterations number?
---@param onEndCallback function?
function detailsFramework.Schedules.LazyExecute(callback, payload, maxIterations, onEndCallback)
assert(type(callback) == "function", "DetailsFramework.Schedules.LazyExecute() param #1 'callback' must be a function.")
maxIterations = maxIterations or 100000
payload = payload or {}
local iterationIndex = 1
local function wrapFunc()
local bIsFinished = callback(payload, iterationIndex, maxIterations)
if (not bIsFinished) then
iterationIndex = iterationIndex + 1
if (iterationIndex > maxIterations) then
detailsFramework:QuickDispatch(onEndCallback, payload)
return
end
C_Timer.After(0, function() wrapFunc() end)
else
detailsFramework:QuickDispatch(onEndCallback, payload)
return
end
end
wrapFunc()
return payload
end
--Schedules a callback function to be executed after a specified time delay.
--It uniquely identifies each scheduled task by an ID, cancel and replace any existing schedules with the same ID.
function detailsFramework.Schedules.AfterById(time, callback, id, ...)
if (not detailsFramework.Schedules.ExecuteTimerTable) then
detailsFramework.Schedules.ExecuteTimerTable = {}
end
local alreadyHaveTimer = detailsFramework.Schedules.ExecuteTimerTable[id]
if (alreadyHaveTimer) then
alreadyHaveTimer:Cancel()
end
local newTimer = detailsFramework.Schedules.NewTimer(time, callback, ...)
detailsFramework.Schedules.ExecuteTimerTable[id] = newTimer
return newTimer
end
--schedule a function to be called after 'time'
--prompt example: create a schedule that runs the function 'variable name' after 'time' amount of seconds
function detailsFramework.Schedules.After(time, callback)
C_Timer.After(time, callback)
end
--schedule a function to be called on the next frame
--prompt example: run 'function name' on next tick
---@param callback function
function detailsFramework.Schedules.RunNextTick(callback)
return detailsFramework.Schedules.After(0, callback)
end
--set a name to a scheduled object
---@param object timer
---@param name string
function detailsFramework.Schedules.SetName(object, name)
object.name = name
4 years ago
end