noita-mapcap/files/libraries/process-runner.lua
David Vogel a2cb806ffa Add progress bar
- Change process runner to return state table
- Add progress bar graphics
- Add progress bar UI
- Fill progress runner context state with more capturing info
2022-07-29 19:42:44 +02:00

119 lines
3.0 KiB
Lua

-- Copyright (c) 2022 David Vogel
--
-- This software is released under the MIT License.
-- https://opensource.org/licenses/MIT
-- A simple library to run/control processes. Specifically made for the Noita map capture addon.
-- This allows only one process to be run at a time in a given context.
-- No idea if this library has much use outside of this mod.
if not async then
require("coroutines") -- Loads Noita's coroutines library from `data/scripts/lib/coroutines.lua`.
end
-------------
-- Classes --
-------------
local ProcessRunner = {}
---@class ProcessRunnerCtx
---@field running boolean|nil
---@field stopping boolean|nil
---@field state table|nil
local Context = {}
Context.__index = Context
-----------------
-- Constructor --
-----------------
---Returns a new process runner context.
---@return ProcessRunnerCtx
function ProcessRunner.New()
return setmetatable({}, Context)
end
-------------
-- Methods --
-------------
---Returns whether some process is running.
---@return boolean
function Context:IsRunning()
return self.running or false
end
---Returns whether the process needs to stop as soon as possible.
---@return boolean
function Context:IsStopping()
return self.stopping or false
end
---Returns a table with information about the current state of the process.
---Will return nil if there is no process.
---@return table|nil -- Custom to the process.
function Context:GetState()
return self.state
end
---Tells the currently running process to stop.
function Context:Stop()
self.stopping = true
end
---Starts a process with the three given callback functions.
---This will just call the tree callbacks in order.
---Everything is called from inside a coroutine, so you can use yield.
---
---There can only be ever one process at a time.
---If there is already a process running, this will just do nothing.
---@param initFunc fun(ctx:ProcessRunnerCtx)|nil -- Called first.
---@param doFunc fun(ctx:ProcessRunnerCtx)|nil -- Called after `initFunc` has been run.
---@param endFunc fun(ctx:ProcessRunnerCtx)|nil -- Called after `doFunc` has been run.
---@param errFunc fun(err:string, scope:"init"|"do"|"end") -- Called on any error.
---@return boolean -- True if process was started successfully.
function Context:Run(initFunc, doFunc, endFunc, errFunc)
if self.running then return false end
self.running, self.stopping, self.state = true, false, {}
async(function()
-- Init function.
if initFunc then
local ok, err = pcall(initFunc, self)
if not ok then
-- Error happened, abort.
if endFunc then pcall(endFunc, self) end
errFunc(err, "init")
self.running, self.stopping = false, false
return
end
end
-- Do function.
if doFunc then
local ok, err = pcall(doFunc, self)
if not ok then
-- Error happened, abort.
errFunc(err, "do")
end
end
-- End function.
if endFunc then
local ok, err = pcall(endFunc, self)
if not ok then
-- Error happened, abort.
errFunc(err, "end")
end
end
self.running, self.stopping, self.state = false, false, nil
end)
return true
end
return ProcessRunner