mirror of
https://github.com/esx-framework/esx_core.git
synced 2026-08-28 17:01:14 +00:00
07b9253401
The processing thread counted the remaining time down in 1000 ms steps and read the global CurrentProgress on every wake, which caused two problems. Any duration that is not a whole number of seconds finished late, since the thread only checked after another full second. The NUI bar runs on wall clock time, so the bar completed and the player then stood frozen waiting for onFinish. Cancelling did not stop the thread either. CancelProgressbar clears CurrentProgress, but the sleeping thread only notices up to a second later, and if a new bar was started in that window it saw a non-nil CurrentProgress and kept decrementing the new one alongside its own thread. The new bar then finished at roughly half its length, firing onFinish early. The run now carries an id, so a thread stops as soon as its run is no longer the current one, and the end is an absolute timestamp rather than a countdown. Measured in game on artifact 25770, requested versus actual onFinish: before: 500 -> 1011, 4200 -> 5042, cancel then 3000 -> 1427 after: 500 -> 519, 4200 -> 4222, cancel then 3000 -> 3058
93 lines
2.9 KiB
Lua
93 lines
2.9 KiB
Lua
---@class ProgressBarAnimation
|
|
---@field public type "anim" | "Scenario"
|
|
---@field public dict? string
|
|
---@field public lib? string
|
|
|
|
---@class ProgressBarOptions
|
|
---@field public animation ProgressBarAnimation
|
|
---@field public FreezePlayer? boolean
|
|
---@field public onFinish? function
|
|
|
|
local CurrentProgress = nil
|
|
local progressCount = 0
|
|
|
|
local function startProcessing(id)
|
|
while CurrentProgress ~= nil and CurrentProgress.id == id do
|
|
if GetGameTimer() < CurrentProgress.finishAt then
|
|
Wait(50)
|
|
else
|
|
ClearPedTasks(ESX.PlayerData.ped)
|
|
if CurrentProgress.FreezePlayer then
|
|
FreezeEntityPosition(ESX.PlayerData.ped, false)
|
|
end
|
|
if CurrentProgress.onFinish then
|
|
CurrentProgress.onFinish()
|
|
end
|
|
CurrentProgress = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
---@param message string
|
|
---@param length? number Timeout in milliseconds
|
|
---@param Options? ProgressBarOptions
|
|
---@return boolean Success
|
|
local function Progressbar(message, length, Options)
|
|
if CurrentProgress then
|
|
return false
|
|
end
|
|
CurrentProgress = Options or {}
|
|
if CurrentProgress.animation then
|
|
if CurrentProgress.animation.type == "anim" then
|
|
ESX.Streaming.RequestAnimDict(CurrentProgress.animation.dict, function()
|
|
TaskPlayAnim(ESX.PlayerData.ped, CurrentProgress.animation.dict, CurrentProgress.animation.lib, 1.0, 1.0, length, 1, 1.0, false, false, false)
|
|
RemoveAnimDict(CurrentProgress.animation.dict)
|
|
end)
|
|
elseif CurrentProgress.animation.type == "Scenario" then
|
|
TaskStartScenarioInPlace(ESX.PlayerData.ped, CurrentProgress.animation.Scenario, 0, true)
|
|
end
|
|
end
|
|
if CurrentProgress.FreezePlayer then
|
|
FreezeEntityPosition(ESX.PlayerData.ped, CurrentProgress.FreezePlayer)
|
|
end
|
|
SendNUIMessage({
|
|
type = "Progressbar",
|
|
length = length or 3000,
|
|
message = message or "ESX-Framework",
|
|
})
|
|
progressCount = progressCount + 1
|
|
CurrentProgress.id = progressCount
|
|
CurrentProgress.length = length or 3000
|
|
CurrentProgress.finishAt = GetGameTimer() + CurrentProgress.length
|
|
|
|
local id = progressCount
|
|
CreateThread(function()
|
|
startProcessing(id)
|
|
end)
|
|
return true;
|
|
end
|
|
|
|
local function CancelProgressbar()
|
|
if not CurrentProgress then
|
|
return
|
|
end
|
|
SendNUIMessage({
|
|
type = "Close",
|
|
})
|
|
ClearPedTasks(ESX.PlayerData.ped)
|
|
if CurrentProgress.FreezePlayer then
|
|
FreezeEntityPosition(ESX.PlayerData.ped, false)
|
|
end
|
|
if CurrentProgress.onCancel then
|
|
CurrentProgress.onCancel()
|
|
end
|
|
CurrentProgress.canceled = true
|
|
CurrentProgress.length = 0
|
|
CurrentProgress = nil
|
|
end
|
|
|
|
ESX.RegisterInput("cancelprog", "[ProgressBar] Cancel Progressbar", "keyboard", "BACK", CancelProgressbar)
|
|
|
|
exports("Progressbar", Progressbar)
|
|
exports("CancelProgressbar", CancelProgressbar)
|