VORP Lib is a modular scripting library for RedM that simplifies game development by providing reusable, instance-based components with automatic cleanup. Designed specifically for VORP Core Framework, it helps developers write cleaner, more efficient scripts while eliminating common issues like memory leaks and global variable pollution
This documentation is for developers who are making scripts for redm, you should also note that this is a work in progress and anything can be changed at any time until the final release.
You cannot Import encrypted files like with escrow etc, only files that aren’t encrypted can be imported.
Allows to import any modules from the lib, a list of modules available can be found here
Copy
local module = Import "modulename" -- no symbolslocal prompts = Import("prompts").Prompts -- every module has a table with the module name as the key for readabilitylocal Lib = Import "prompts"local Prompts = Lib.Prompts -- [[@as PROMPTS]] -- for intellisense
This module is used to create entities like peds, vehicles, objects, etc, it has a baseclass for all entities and sub classes for each entity type
all creations are instanced objects every creation will have its own instance and wont be shared with other scripts since its imported to your script
when you restart your resource the entities will be removed for easy developmentit has a entity tracker if you wish to track the entities from other scripts (see collector file) for exports
BASECLASS
PED SUBCLASS
VEHICLE SUBCLASS
OBJECT SUBCLASS
Shared
This is the baseclass for (peds, vehicles, objects) subclasses you can use these methods bellow or use directly the natives
The function to will be called when the ped is deleted
Copy
-- Example -- Import the entities module local Entity = Import 'entities' --[[@as ENTITY]] local ped = Entity.Ped:Create({ Model = 'A_C_COW', Pos = vector4(0, 0, 0, 0), IsNetworked = true, Options = { PlaceOnGround = true, OutfitPreset = 0, }, OnCreate = function(self) print('Ped created use your own logic here, handle: ', self:GetHandle()) end, OnDelete = function(handle, netid) print('Ped deleted use your own logic here, handle: ', handle, 'netid: ', netid) end }) -- methods you can use local handle = ped:GetHandle() ped:Delete()
Vehicle
This sub class is used to create vehicles (inherits from Entity base class ) these are instanced objects every creation will have its own instance
Below are the methods available for the vehicle class
The map module is used to create blips for now but will be expanded to include more map related features
when you restart your resource the blips will be removed for easy development
Map
Blip
This is the baseclass for blips, you can use these methods below or use directly the natives
Returns the color value's corresponding to the color name's
Copy
-- single string or multiple colors can be requested just for ease of use local blue, red, yellow = Map.Blips:GetBlipColor({ 'blue', 'red', 'yellow' }) blip:AddModifierColor(blue) -- or string "blue"
The function that will be called when the blip is created
Copy
-- Example -- Import the blips module local Map = Import 'blips' --[[@as BLIPS]] local blip = Map.Blips:Create('radius', { -- type can be entity, coords, area, radius Entity = ped, -- if type is entity, you need to provide a handle Pos = vector3(2865.88, 475.38, 66.09), -- position Radius = 50.0, -- if type is radius or area P7 = 0, -- optional default is 0 Blip = 1673015813, -- blip hash the style of the blip Scale = vector3(1.0, 1.0, 1.0), -- for type area only Options = { -- optional sprite = 1, --string or integer if type is entity or coords name = 'Test', modifier = 'BLIP_MODIFIER_MP_COLOR_1', -- int or string color = 'blue', -- internal color name }, OnCreate = function(self) print('Created', self:GetHandle()) local blue, red, yellow = self:GetBlipColor({ 'blue', 'red', 'yellow' }) self:AddModifier(red) end }) local handle = blip:GetHandle() blip:Remove()
this module is used to create input controls for your resource, single or multiple , without the user having to create loops and a bunch of code
all creations are instanced objects every creation will have its own instance and wont be shared with other scripts since its imported to your script
when you restart your resource the inputs will be removed for easy development
If true, input will start automatically after registration, useful when you set state to false and start this when player is near something or character is selected
Copy
-- Example -- Import the inputs module local controls = Import 'inputs' --[[@as INPUTS]] -- Multiple input support local inputs = { { inputType = "Press", key = "E" }, { inputType = "Hold", key = "W" }, { inputType = "Release", key = "S" }, } local input = controls.Inputs:Register(inputs,function(input, customParams) if input.key == "E" then print("E was pressed") elseif input.key == "W" then print("W is being held") elseif input.key == "S" then print("S was released") end end, true) -- auto start on register input:Destroy()
this module is used to create prompts with coordinate-based activation, multiple prompts can be grouped together and managed as one unit
all creations are instanced objects every creation will have its own instance and wont be shared with other scripts since its imported to your script
when you restart your resource the prompts will be removed for easy development
If true, prompts will start automatically after registration (defaults to false) useful when you set state to false and start this when player is near something or character is selected
Copy
-- Example -- Import the prompts module local Game = Import 'prompts' --[[@as PROMPTS]] local data = { locations = { { -- index 1 coords = vector3(2868.43, 480.19, 65.02), -- distance based prompts label = 'group label', -- group label distance = 2.0, -- distance from coords marker = { -- optional marker type = 0x94FDAE17, color = { r = 0, g = 255, b = 0, a = 96 }, distance = 4.0, scale = { x = 2.0, y = 2.0, z = 0.5 }, } } }, sleep = 700, -- sleep time when not in range prompts = { -- group prompts or single prompt { type = 'Press', key = 'G', label = 'press', mode = 'Standard' }, { type = 'Hold', key = 'E', label = 'hold', mode = 'Hold', holdTime = 3000 } } } local prompt = Game.Prompts:Register(data, function(prompt, index , self) -- first array, allows to use one location with multiple prompts if index == 1 then if prompt.key == 'G' then print('G pressed') elseif prompt.key == 'E' then print('E held for 3 seconds') end end end, true) -- auto start on register prompt:Destroy() -- the lib it self will destroy any prompt on script restart
this module is used to register client-side/server-side commands with permissions, suggestions, and argument validation
all creations are instanced objects every creation will have its own instance and wont be shared with other scripts since its imported to your script
when you restart your resource the commands will be removed for easy development, if a command is active and suggestion hasnt been marked to add on register, it will be added on character selected automatically
Client
Server
Command
use these methods below to manage command controls
Whether to add chat suggestion when registering the command, use this only on runtime, by default when player selects character suggestion is added automatically
If true, command starts automatically after registration, useful when you set state to false and start this when player is near something or character is selected
Copy
-- Example -- Import the commands module local Commands = Import 'commands' --[[@as COMMANDS]] local command = Commands.Command:Register("mycommand", { Suggestion = { -- optional Description = "My custom command description", Arguments = { -- if type is number or integer, it will be converted to a number -- if type is message, it will give it as a message { name = "playerId", help = "Target player ID", type = "integer", required = true }, { name = "amount", help = "Amount value", type = "number", required = true }, { name = "message", help = "Optional message", type = "message"} } }, Permissions = { -- optional Ace = "group.admin" -- Restrict to admin group, or remove for public command }, OnExecute = function(args, rawCommand, instance) print("Command executed with args:", json.encode(args)) print("Player ID:", args[1]) -- integer type print("Amount:", args[2]) -- number type print("Message:", args[3]) -- message type (remaining args combined) end, OnError = function(errorType) if errorType == 'missing_arguments' then print('Usage: /mycommand <playerId> <amount> [message]') elseif errorType == 'missing_permission' then print('You do not have permission to use this command') elseif errorType == 'command_active' then print('Command is currently paused') end end }, true) -- Auto-start -- Control methods command:Start(true) -- Start and add suggestion if not have been added yet command:Destroy() -- Clean up completely -- Argument types: -- "integer" - converts to number (whole numbers) -- "number" - converts to number (decimals allowed) -- "message" - combines remaining arguments into string -- (no type) - keeps as string -- Error types: -- "missing_arguments" - Required argument not provided -- "missing_permission" - User lacks required permissions -- "command_active" - Command is paused -- "missing_target" - Target not found (if applicable)
Command
use these methods below to manage server command controls
If true, command starts automatically after registration (defaults to false)
Copy
-- Example -- Import the server commands module local LIB = Import 'commands' --[[@as COMMANDS]] local command = LIB.Command:Register("commandName", { Suggestion = { -- optional Description = "Admin command with complex permissions", Arguments = { { name = "playerId", help = "Target player ID", type = "integer", required = true }, { name = "amount", help = "Amount value", type = "number", required = true }, { name = "message", help = "Optional message", type = "message" } } }, Permissions = { -- optional Ace = "group.admin", -- ACE permission (overrides others) leave false if you dont want to use ace permissions Jobs = { -- optional Police = { -- jobname [0] = false, -- Rank 0 not allowed [1] = true, -- Rank 1+ allowed }, Sheriff = true -- All ranks allowed }, Groups = { -- optional users = { admin = true, moderator = true }, characters = { gang_leader = true } }, CharIds = { -- optional [123] = true, -- Specific character ID [456] = true } }, OnExecute = function(source, args, rawCommand, instance) print("Command executed by source:", source) print("Player ID:", args[1]) -- player type (validated) print("Amount:", args[2]) -- number type print("Message:", args[3]) -- message type end, OnError = function(errorType) if errorType == 'missing_arguments' then print('Usage: /admincommand <playerId> <amount> [message]') elseif errorType == 'missing_permission' then print('You do not have permission to use this command') elseif errorType == 'missing_job' then print('You do not have the required job') elseif errorType == 'missing_grade' then print('You do not have the required job rank') elseif errorType == 'missing_group' then print('You do not have the required group') elseif errorType == 'missing_character' then print('Your character is not authorized') elseif errorType == 'missing_user' then print('User not found or console command not supported') elseif errorType == 'command_active' then print('Command is currently paused') end end }, true) -- Auto-start -- Control methods command:Destroy() -- Clean up completely -- Server-specific features: -- - Automatic ACE permission management -- - Complex job/grade validation -- - Character and user group permissions -- - Per-player suggestion management -- - Console command restriction (source = 0) -- Error types (additional server-side): -- "missing_user" - User not found or console command -- "missing_job" - Player doesn't have required job -- "missing_grade" - Player doesn't have required job rank -- "missing_group" - Player doesn't have required group -- "missing_character" - Character not authorized -- "missing_state" - State validation failed -- Permission priority: -- 1. ACE permissions (highest) all others will be ignored -- 2. Job permissions -- 3. Group permissions -- 4. Character ID permissions
this module is used to create coordinate-based enter/exit areas with radius detection, multiple points can be registered and managed independently
all creations are instanced objects every creation will have its own instance and wont be shared with other scripts since its imported to your script
when you restart your resource the points will be removed for easy development
If true, point system starts automatically after registration other wise use the Start method to start the point system
Copy
-- Example -- Import the points module local GamePoints = Import 'points' --[[@as POINTS]] local points = GamePoints.Points:Register({ Arguments = { { id = 'bank_entrance', -- unique identifier center = vector3(2843.49, 474.49, 64.03), -- center coordinates radius = 15.0, -- detection radius wait = 500, -- check interval (ms) debug = true, -- show debug marker deActivate = true, -- start active? remove to start active }, { id = 'shop_door', center = vector3(2884.03, 484.19, 66.73), radius = 10.0, wait = 300, debug = true, }, }, OnEnter = function(point, distance) print("Entered point:", point.id, "Distance:", distance) if point.id == 'bank_entrance' then print("Welcome to the bank!") elseif point.id == 'shop_door' then print("Welcome to the shop!") end end, OnExit = function(point, distance) print("Exited point:", point.id, "Distance:", distance) if point.id == 'bank_entrance' then print("Left the bank area") elseif point.id == 'shop_door' then print("Left the shop area") end end }, true) -- Auto-start -- Control methods points:PausePoint('shop_door') -- Pause specific point points:ResumePoint('shop_door') -- Resume specific point points:RemovePoint('bank_entrance') -- Remove specific point -- Check point status local isActive = points:IsPointActive('shop_door') local isInside = points:IsPointInside('shop_door') local isOutside = points:IsPointOutside('shop_door') -- System control points:Pause() -- Pause entire system points:Resume() -- Resume entire system points:Destroy() -- Clean up completely
this module is used to create polygon, circle, or box shaped detection zones with height filtering, debug rendering, and enter/inside/exit callbacks
all creations are instanced objects every zone instance is private to the script that imports it and will be removed automatically when the resource restarts
PolyZones
PolyZone
use these methods below to register and manage advanced zone shapes
Stops the zone, removes it from the manager, and cleans it up
Copy
local PolyZones = Import('polyzones').PolyZones local zone = PolyZones:Register({ id = 'temp_zone', type = 'circle', center = vector3(-100.0, 120.0, 40.0), radius = 3.0, }, true) PolyZones:Destroy(zone)
this module is used to register game event listeners that can capture and process native game events with automatic data parsing
all creations are instanced objects every creation will have its own instance and wont be shared with other scripts since its imported to your script
when you restart your resource the event listeners will be removed for easy development
When enabled, logs all events in the group. Use eventsToIgnore to filter out noise.
Copy
-- Enable dev mode and ignore specific events event:DevMode(true, {"EVENT_PED_CREATED", "EVENT_PED_DESTROYED"}) -- Enable dev mode for all events event:DevMode(true) -- Disable dev mode event:DevMode(false)
If true, event listener starts automatically after registration, otherwise use Start method
Copy
-- Example -- Import the events module local Game = Import 'events' --[[@as EVENTS]] -- Register event with automatic data parsing local event = Game.Events:Register('EVENT_PED_CREATED', 0, function(data) print("Ped created with data:", json.encode(data, {indent = true})) end, true) -- Auto-start -- Developer mode for debugging, dont fire this events when in dev mode event:DevMode(true, {"EVENT_PED_CREATED","EVENT_VEHICLE_CREATED"}) -- Enable dev mode, ignore these events -- dev mode enables all events to be triggered -- Clean up event:Destroy()
this module provides JavaScript-like DataView functionality for handling binary data in Lua with support for various data types and endianness
this module is based on gottfriedleibniz’s DataView implementation providing efficient binary data manipulation
Returns a DataView instance wrapping the existing data
Copy
-- Wrap existing binary data local existingData = string.pack("i4f", 100, 2.718) local wrappedView = Data.DataView.Wrap(existingData) -- Read from wrapped data local intVal = wrappedView:GetInt32(0) -- 100 local floatVal = wrappedView:GetFloat32(4) -- 2.718
-- Create buffer with mixed data local buffer = Data.DataView.ArrayBuffer(32) buffer:SetInt32(0, 123) buffer:SetFloat32(4, 4.56) buffer:SetInt16(8, 789) -- Create stream for sequential reading local stream = Data.DataView.DataStream.New(buffer) -- Read sequentially (offset advances automatically) local int1 = stream:Int32() -- 123, offset now at 4 local float1 = stream:Float32() -- 4.56, offset now at 8 local int2 = stream:Int16() -- 789, offset now at 10 print("Sequential read:", int1, float1, int2)
this module provides utility functions for loading various game assets like models, animations, textures, and more with automatic cleanup and timeout handling
all functions handle the loading process with proper validation and error handling, preventing common issues with asset streaming
Streaming
Asset Loading
use these functions below to load various game assets with automatic cleanup
Loads world area around entity - use carefully as it can cause crashes with too many MLOs
Import
import the streaming module
Copy
-- Import the streaming module local Assets = Import 'streaming' --[[@as STREAMING]] -- Use any function Assets.Streaming.LoadModel('A_C_BEAR_01') Assets.Streaming.LoadAnimDict('amb@world_human_drinking@coffee@male@idle_a')
this module provides a complete object-oriented programming system for Lua with classes, inheritance, private members, and automatic getters/setters
supports both traditional Lua OOP patterns and modern structured approaches with automatic property management
was inspired by JavaScript classes
Class
OOP System
use these methods below to create classes with full OOP support
Returns a new class that can create instances with :New()
Copy
-- Import the class module local Lib = Import 'class' --[[@as CLASS]] -- Create a basic class local MyClass = Lib.Class:Create({ constructor = function(self, name) self.name = name end, getName = function(self) return self.name end }, "MyClass") -- Create instance local instance = MyClass:New("Test") print(instance:getName()) -- "Test"
Traditional Lua example
Copy
local MyClass = Lib.Class:Create({},"MyClass") function MyClass:constructor(name) self.name = name end function MyClass:getName() return self.name end local instance = MyClass:New("Test") print(instance:getName()) -- "Test"
Used within a constructor to call the parent class constructor.
Copy
local Ped = Lib.Class:Create(Entity, "Ped") function Ped:constructor(id, model) self:super(id) -- Call parent constructor self.model = model end
Automatic Properties
Define automatic getters and setters for properties using get and set tables.
Copy
local Person = Lib.Class:Create({ constructor = function(self, name, age) self.name = name self.age = age end, -- can be used to organize your code as well just like JS classes get = { name = function(self) return self.name:upper() -- Always return uppercase end, age = function(self) return self.age end, isAdult = function(self) return self.age >= 18 end }, -- can be used to organize your code as well just like JS classes set = { name = function(self, value) if type(value) ~= "string" then error("Name must be a string") end self.name = value end, age = function(self, value) if type(value) ~= "number" or value < 0 then error("Age must be a positive number") end self.age = value end } }) local person = Person:New("john", 25) -- Using getters print(person.name) -- "JOHN" (automatic uppercase) print(person.isAdult) -- true -- Using setters person.name = "jane" -- Validates and stores person.age = 30 -- Validates and stores
Private Properties & Methods
Members starting with underscore _ are private and can only be accessed from within the same class.
Copy
local BankAccount = Lib.Class:Create({ constructor = function(self, accountNumber, initialBalance) self._accountNumber = accountNumber -- Private self._balance = initialBalance -- Private self.accountType = "Checking" -- Public end, -- Public method that accesses private members getBalance = function(self) self:_validateAccess() -- Private method call return self._balance end, deposit = function(self, amount) if amount > 0 then self._balance = self._balance + amount return true end return false end, -- Private method _validateAccess = function(self) print("Validating access to account " .. self._accountNumber) end, -- Private method _calculateInterest = function(self) return self._balance * 0.01 end }, "BankAccount") local account = BankAccount:New("12345", 1000) -- ✅ Public access print(account:getBalance()) -- 1000 account:deposit(500) -- ❌ Private access will error -- print(account._balance) -- ERROR -- account:_validateAccess() -- ERROR
Inheritance & Privacy
Private members are class-specific and cannot be accessed by subclasses.
Copy
local Vehicle = Lib.Class:Create({ constructor = function(self, model) self._engine = "V8" -- Private to Vehicle self.model = model -- Public end, getEngineInfo = function(self) return "Engine: " .. self._engine -- ✅ Same class access end, _startEngine = function(self) print("Starting " .. self._engine .. " engine") end }) local Car = Lib.Class:Create(Vehicle, "Car") function Car:constructor(model, doors) self:super(model) self.doors = doors -- self._engine = "Modified" -- ❌ Would error - can't access parent private end function Car:tryAccessPrivate() -- ❌ Cannot access parent's private members -- local engine = self._engine -- ERROR -- self:_startEngine() -- ERROR print("Cannot access parent private members") end local car = Car:New("Mustang", 2) print(car:getEngineInfo()) -- ✅ "Engine: V8" (via public method) car:tryAccessPrivate() -- Shows privacy enforcement
Complete Example
comprehensive class system example
Copy
-- Import the class module local Lib = Import 'class' --[[@as CLASS]] -- Base Entity class local Entity = Lib.Class:Create({ constructor = function(self, data) self._id = data.id or 0 -- Private ID self._position = data.pos or vector3(0,0,0) -- Private position self.name = data.name or "Entity" -- Public name self._created = os.time() -- Private creation time end, -- Public methods getId = function(self) return self._id end, getPosition = function(self) return self._position end, setPosition = function(self, pos) self._position = pos self:_onPositionChanged() -- Private method call end, getAge = function(self) return os.time() - self._created end, -- Private methods _onPositionChanged = function(self) print("Entity " .. self._id .. " moved to " .. tostring(self._position)) end, -- Automatic getters/setters get = { displayName = function(self) return self.name .. " (#" .. self._id .. ")" end }, set = { name = function(self, value) if type(value) ~= "string" or #value == 0 then error("Name must be a non-empty string") end self.name = value end } }, "Entity") -- Ped class inheriting from Entity local Ped = Lib.Class:Create(Entity, "Ped") function Ped:constructor(data) self:super(data) -- Call parent constructor self._model = data.model or "A_M_M_FARMER_01" -- Private model self._health = data.health or 100 -- Private health self.faction = data.faction or "Civilian" -- Public faction end function Ped:spawn() local pos = self:getPosition() local handle = CreatePed(joaat(self._model), pos.x, pos.y, pos.z, 0.0, true, false, false, false) self._handle = handle print("Spawned " .. self.displayName .. " at " .. tostring(pos)) return handle end function Ped:damage(amount) self._health = math.max(0, self._health - amount) if self._health <= 0 then self:_onDeath() end end -- Private method function Ped:_onDeath() print(self.displayName .. " has died") if self._handle then DeletePed(self._handle) end end -- Getters for private properties Ped.get.health = function(self) return self._health end Ped.get.model = function(self) return self._model end -- Usage local ped = Ped:New({ id = 123, name = "John Marston", pos = vector3(100, 200, 300), model = "CS_JOHNMARSTON", health = 150, faction = "Van der Linde Gang" }) -- Public interface print(ped.displayName) -- "John Marston (#123)" print("Health:", ped.health) -- 150 (via getter) print("Age:", ped:getAge(), "seconds old") ped:setPosition(vector3(150, 250, 350)) ped:spawn() ped:damage(50) print("Health after damage:", ped.health) -- 100 -- Validation works ped.name = "Arthur Morgan" -- ✅ Valid -- ped.name = "" -- ❌ Would error -- Privacy enforced -- print(ped._health) -- ❌ Would error -- ped:_onDeath() -- ❌ Would error
Traditional Lua Style
using traditional lua function syntax
Copy
local Lib = Import 'class' --[[@as CLASS]] -- Create class with traditional Lua methods local Timer = Lib.Class:Create({},"Timer") function Timer:constructor(name, duration) self.name = name or "Timer" self._startTime = nil self._duration = duration or 5000 self._isRunning = false end function Timer:start() self._startTime = GetGameTimer() self._isRunning = true print(self.name .. " started for " .. self._duration .. "ms") end function Timer:stop() self._isRunning = false print(self.name .. " stopped") end function Timer:isExpired() if not self._isRunning or not self._startTime then return false end return (GetGameTimer() - self._startTime) >= self._duration end function Timer:getTimeLeft() if not self._isRunning or not self._startTime then return 0 end local elapsed = GetGameTimer() - self._startTime return math.max(0, self._duration - elapsed) end -- Usage local timer = Timer:New("Countdown", 10000) timer:start() -- Check in a loop or thread CreateThread(function() while not timer:isExpired() do print("Time left:", timer:getTimeLeft() .. "ms") Wait(1000) end print("Timer expired!") timer:stop() end)
utility classes for control flow, timing, and conditional execution
provides Switch-case patterns, repeating intervals, and one-time timeouts with full control over execution state
shared between server and client environments
Switch
Interval
Timeout
Switch
use these utilities for advanced control flow and timing operations
creates a repeating interval that executes a function at specified intervals
Copy
-- Import the functions module local Lib = Import 'functions' --[[@as FUNCTIONS]] -- Create an interval that runs every 5 seconds local healthCheck = Lib.SetInterval(function(self, playerId) local player = GetPlayerPed(playerId) if player and DoesEntityExist(player) then local health = GetEntityHealth(player) print("Player " .. playerId .. " health: " .. health) self:Destroy() -- destroy the interval end end, 5000,{GetPlayerServerId(PlayerId())}, true)
creates a one-time delayed execution that can be controlled
Copy
-- Import the functions module local Lib = Import 'functions' --[[@as FUNCTIONS]] -- Create a timeout that executes after 10 seconds local delayedAction = Lib.SetTimeout(function(message, playerId) print("Delayed message: " .. message) end, 10000, {"Welcome to the server!", PlayerId()})
Table with the colors for the progress bar startColor and endColor are the colors for the text and backgroundColor and fillColor are the colors for the background and the fill of the progress bar image
the result of the progress bar true or false if false the progress bar was cancelled
Copy
local data = { text = 'Some text here', colors = { -- for text startColor = 'white', -- starting color of the text endColor = 'black', -- ending color of the text -- these colors are filters they dont really represent the color that well but its an option if you want to change it -- for background -- https://colorpicker.dev/#21d70d use this website choose hwb and its the first number just add deg to it like this 330deg -- backgroundColor = '0deg', -- Changes grey bar to blue-ish --fillColor = '120deg', -- Changes white bar to green-ish }, duration = 5000, type = 'linear', -- only linear is avaliable for now position = { top = 90, left = 50 }, -- in % for position on the screen image = 'score_timer_extralong', -- only png this is optional you can add your own image , images must be in this script images folder}-- SYNClocal result = exports.vorp_lib:progressStart(data)if not result then print('cancelled')else print('completed')end--OR ASYNCexports.vorp_lib:progressStart(data, function(result) if result then print('Progress bar completed') else print('Progress bar cancelled') endend)