mirror of
https://github.com/esx-framework/esx_core.git
synced 2026-08-29 01:08:54 +00:00
f98a6e2245
Registered jobs are third party callbacks and were invoked bare. An error inside one propagates out of OnTime into Tick, so Tick never reaches its SetTimeout(60000, Tick) at the end and nothing ever reschedules it. From that moment cron is dead for every resource on the server - paychecks, cleanups, anything registered through cron:runAt - until a restart. The operator sees one stack trace and then silence. Wrapping the callback keeps the loop and the reschedule intact, and names the job that failed. Simulated the scheduler chain in standalone Lua with three jobs where the first raises: before, the tick died before SetTimeout and the two later jobs never ran again; after, the failure is reported and both later jobs fire.
79 lines
2.2 KiB
Lua
79 lines
2.2 KiB
Lua
---@class CronJob
|
|
---@field h number
|
|
---@field m number
|
|
---@field cb function|table
|
|
|
|
---@type CronJob[]
|
|
local cronJobs = {}
|
|
---@type number|false
|
|
local lastTimestamp = false
|
|
|
|
---@param h number
|
|
---@param m number
|
|
---@param cb function|table
|
|
function RunAt(h, m, cb)
|
|
cronJobs[#cronJobs + 1] = {
|
|
h = h,
|
|
m = m,
|
|
cb = cb,
|
|
}
|
|
end
|
|
|
|
---@return number
|
|
function GetUnixTimestamp()
|
|
return os.time()
|
|
end
|
|
|
|
---@param timestamp number
|
|
function OnTime(timestamp)
|
|
for i = 1, #cronJobs, 1 do
|
|
local scheduledTimestamp = os.time({
|
|
hour = cronJobs[i].h,
|
|
min = cronJobs[i].m,
|
|
sec = 0, -- Assuming tasks run at the start of the minute
|
|
day = os.date("%d", timestamp),
|
|
month = os.date("%m", timestamp),
|
|
year = os.date("%Y", timestamp),
|
|
})
|
|
|
|
if timestamp >= scheduledTimestamp and (not lastTimestamp or lastTimestamp < scheduledTimestamp) then
|
|
local d = os.date('*t', scheduledTimestamp).wday
|
|
|
|
if not pcall(cronJobs[i].cb, d, cronJobs[i].h, cronJobs[i].m) then
|
|
print(("[^1ERROR^7] cron job at ^5%02d:%02d^7 errored, skipping it"):format(cronJobs[i].h, cronJobs[i].m))
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
---@return nil
|
|
function Tick()
|
|
local timestamp = GetUnixTimestamp()
|
|
|
|
if not lastTimestamp or os.date("%M", timestamp) ~= os.date("%M", lastTimestamp) then
|
|
OnTime(timestamp)
|
|
lastTimestamp = timestamp
|
|
end
|
|
|
|
SetTimeout(60000, Tick)
|
|
end
|
|
|
|
lastTimestamp = GetUnixTimestamp()
|
|
Tick()
|
|
|
|
---@param h number
|
|
---@param m number
|
|
---@param cb function|table
|
|
AddEventHandler("cron:runAt", function(h, m, cb)
|
|
local invokingResource = GetInvokingResource() or "Unknown"
|
|
local typeH = type(h)
|
|
local typeM = type(m)
|
|
local typeCb = type(cb)
|
|
|
|
assert(typeH == "number", ("Expected number for h, got %s. Invoking Resource: '%s'"):format(typeH, invokingResource))
|
|
assert(typeM == "number", ("Expected number for m, got %s. Invoking Resource: '%s'"):format(typeM, invokingResource))
|
|
assert(typeCb == "function" or (typeCb == "table" and type(getmetatable(cb)?.__call) == "function"), ("Expected function for cb, got %s. Invoking Resource: '%s'"):format(typeCb, invokingResource))
|
|
|
|
RunAt(h, m, cb)
|
|
end)
|