Concepts
OOP
vRP 2 Object-Oriented Programming uses the Luaoop library.
By using OOP, instead of loading resources communicating with vRP through Proxy, extensions can directly be loaded into the vRP resource context. Each extension keeps its data and is able to access other extensions data and methods, making extending vRP easier, more powerful (we can access stuff even if not exposed) and with less overhead. |
Asynchronous
vRP 2 extensively uses asynchronous tasks in a transparent way using coroutines, some functions may not return immediately (or never).
DB driver
A DB driver is a class handling MySQL queries. When the vRP MySQL API is used, the DB driver methods are called to process the queries. If the DB driver is not loaded yet, queries will be cached until loaded.
Proxy and Tunnel
The proxy library is used to call other resources functions through a proxy event.
The idea behind tunnel is to easily access any exposed server function from any client resource, and to access any exposed client function from any server resource.
Good practice is to get the interface once and set it as a global. If we want to get multiple times the same interface from the same resource, we need to specify a unique identifier (the name of the resource + a unique id for each one). |
Tunnel and Proxy are blocking calls in the current coroutine until the values are returned, to bypass this behaviour, especially for the Tunnel to optimize speed (ping latency of each call), use _ as a prefix for the function name (Proxy/Tunnel interfaces should not have functions starting with _ ). This will discard the returned values, but if we still need them, we can make normal calls in a new Citizen thread with Citizen.CreateThreadNow or async to have non-blocking code.
|
Also remember that Citizen event handlers (used by Proxy and Tunnel) may not work while loading the resource; to use the Proxy at loading time, we will need to delay it with Citizen.CreateThread or a SetTimeout .
|
Data
Multi-server and character
vRP 2 has multi-server and multi-character support. Each server has a string identifier.
Players can use another character after spawned, thus extensions should properly handle character load/unload events and check if the character is ready. |
Key-value stores
vRP provides a key-value API to store custom binary data in the database. A string key is associated to a binary value (ex: a Lua string). The key is a VARCHAR(100)
and the value a BLOB
(probably 65535 bytes).
global |
GData |
per server |
SData |
per user |
UData |
per character |
CData |
vRP core tables, as core files, should not be modified for extensions compatibility. New tables and queries should be created if the key-value system is not powerful enough. |
Core
vRP core modules data may be stored in specific MySQL tables or using the key-value stores. For the latter, the keys will probably be prefixed by vRP
and the binary data will use the MessagePack format.
Script loading
To use vRP 2, a script must be loaded in the vRP resource context of a specific side.
-- include `@vrp/lib/utils.lua` in `__resource.lua` (for the targeted side)
local Proxy = module("vrp", "lib/Proxy")
local vRP = Proxy.getInterface("vRP")
vRP.loadScript("my_resource", "vrp") -- load "my_resource/vrp.lua"
The content of vrp.lua
is now executed in the vRP context and can now use the API.
vRP.loadScript() is a proxy to call module() in the vRP context.
|
Extension
An extension is a class extending vRP.
Two versions of the same extension (same name) can be loaded: for the server-side and the client-side. They will be able to interact with each other through the tunnel
/remote
interfaces.
local MyExt = class("MyExt", vRP.Extension)
Loaded extensions are accessibles through the vRP instance:
vRP.EXT.MyExt:test()
You can see how an extension is made by looking at the code of vRP modules or https://github.com/vRP-framework/vRP-basic-mission. |
User
Extensions can extend User properties/methods with a User class (constructor is called).
To not conflict with other extensions, make sure the added properties and methods have a very specific name or prefix. |
MyExt.User = class("User")
Event
Extensions can listen to global events by defining methods in the event
table.
MyExt.event = {}
function MyExt.event:playerSpawn(user, first_spawn)
end
Events marked with (sync) in the documentation may be called using vRP:triggerEventSync which will wait for the listeners to complete, meaning that listeners must return (mostly in a short time frame) in order to let the execution continue normally.
|
Proxy and Tunnel
Extensions can listen to proxy/tunnel calls by defining methods in the proxy
or tunnel
table.
MyExt.proxy = {}
function MyExt.proxy:getInfo()
end
-- client-side
MyExt.tunnel = {}
function MyExt.tunnel:test()
end
The proxy interface generated will be accessible from other resources like this:
local my_ext = Proxy.getInterface("vRP.EXT.MyExt")
local info = my_ext.getInfo()
Extensions don’t need and should not use proxy between them. |
The tunnel is accessible (from the client-side or server-side extension) through the remote
table.
-- server-side
function MyExt.event:playerSpawn(user, first_spawn)
self.remote._test(user.source)
end
-- client-side
function MyExt.event:playerDeath()
self.remote._test()
end
Profiler
vRP embeds ELProfiler to profile Lua code, vRP and resources based on it. When a resource loads @vrp/lib/utils.lua
(which is the case for resources based on vRP), it will setup itself to be recorded by the profiler.
To use the profiler, the module must be enabled in cfg/modules.lua
. This will keep track of created coroutines to profile them. The overhead is probably small, thus it can be enabled on a live server.
Two options are available (with permissions) in the main and admin menus to respectively profile the client-side or server-side.
Profiling has an overhead, but mostly because of the Lua debug hook. Being a statistical/sampling profiler, profiling a long period of time is fine (low memory usage). |
General API
utils
lib/utils.lua
defines some useful globals.
-- side detection
SERVER -- boolean
CLIENT -- boolean
-- load a lua resource file as module (for a specific side)
-- rsc: resource name
-- path: lua file path without extension
module(rsc, path)
class -- Luaoop class
-- create an async returner or a thread (Citizen.CreateThreadNow)
-- func: if passed, will create a thread, otherwise will return an async returner
async(func)
-- convert Lua string to hexadecimal
tohex(str)
-- basic deep clone function (doesn't handle circular references)
clone(t)
parseInt(v)
-- will remove chars not allowed/disabled by strchars
-- allow_policy: if true, will allow all strchars, if false, will allow everything except the strchars
sanitizeString(str, strchars, allow_policy)
splitString(str, sep)
Proxy and Tunnel
Proxy
-- add event handler to call interface functions
-- name: interface name
-- itable: table containing functions
Proxy.addInterface(name, itable)
-- get a proxy interface
-- name: interface name
-- identifier: (optional) unique string to identify this proxy interface access; if nil, will be the name of the resource
Proxy.getInterface(name, identifier)
Tunnel
-- set the base delay between Triggers for a destination
-- dest: player source
-- delay: milliseconds (0 for instant trigger)
Tunnel.setDestDelay(dest, delay)
-- bind an interface (listen to net requests)
-- name: interface name
-- interface: table containing functions
Tunnel.bindInterface(name,interface)
-- get a tunnel interface to send requests
-- name: interface name
-- identifier: (optional) unique string to identify this tunnel interface access; if nil, will be the name of the resource
Tunnel.getInterface(name,identifier)
Interface
-
interface defined function names should not start with an underscore (
_
) -
the tunnel server-side call requires the player source as first parameter
-
the tunnel server-side called function can use the global
source
(correct until a TriggerEvent/yield/etc) as the remote player source -
using an underscore to call a remote function interface ignores (no wait) the returned values
-- PROXY any side, TUNNEL client-side
-- call and wait for returned values
-- ...: arguments
-- return values
interface.func(...)
-- call without waiting
-- ...: arguments
interface._func(...)
-- TUNNEL server-side
-- call and wait for returned values
-- ...: arguments
-- return values
interface.func(player, ...) -- or _func to ignore returned values
DBDriver
-- called when the driver is initialized (connection), should return true on success
-- db_cfg: cfg/base.lua .db config
DBDriver:onInit(db_cfg)
-- should prepare the query (@param notation)
DBDriver:onPrepare(name, query)
-- should execute the prepared query
-- params: map of parameters
-- mode:
--- "query": should return rows, affected
--- "execute": should return affected
--- "scalar": should return a scalar
DBDriver:onQuery(name, params, mode)
Extension
self.remote -- tunnel interface to other network side
-- level: (optional) level, 0 by default
Extension:log(msg, level)
Extension:error(msg)
User
User inherits from all extensions sub-class User (if registered before the first user instantiation).
self.source
self.name -- FiveM name (may be steam name)
self.id
self.cid -- character id
self.endpoint -- FiveM endpoint
self.data -- user data
self.cdata -- character data
self.loading_character -- flag
self.use_character_action -- action delay
self.spawns -- spawn count
-- return true if the user character is ready (loaded, not loading)
User:isReady()
User:save()
-- return characters id list
User:getCharacters()
-- return created character id or nil if failed
User:createCharacter()
-- use character
-- return true or false, err_code
-- err_code:
--- 1: delay error, too soon
--- 2: already loading
--- 3: invalid character
User:useCharacter(id)
-- delete character
-- return true or false on failure
User:deleteCharacter(id)
vRP
Shared
self.EXT -- map of name => ext
self.modules -- cfg/modules
vRP.Extension
-- register an extension
-- extension: Extension class
vRP:registerExtension(extension)
-- trigger event (with async call for each listener)
vRP:triggerEvent(name, ...)
-- trigger event and wait for all listeners to complete
vRP:triggerEventSync(name, ...)
-- msg: log message
-- suffix: (optional) category, string
-- level: (optional) level, 0 by default
vRP:log(msg, suffix, level)
-- msg: error message
-- suffix: optional category, string
vRP:error(msg, suffix)
extensionLoad(extension)
|
called when an extension is loaded, passing the extension instance (can be used to initialize with another extension when loaded before the latter) |
Server
self.cfg -- cfg/base config
self.lang -- loaded lang (https://github.com/ImagicTheCat/Luang)
self.users -- map of id => User
self.pending_users -- pending user source update (first spawn), map of ids key => user
self.users_by_source -- map of source => user
self.users_by_cid -- map of character id => user
-- db/SQL API
self.db_drivers
self.db_driver
self.db_initialized
vRP.DBDriver
-- return identification string for a specific source
vRP.getSourceIdKey(source)
vRP.getPlayerEndpoint(player)
vRP.getPlayerName(player)
-- register a DB driver
-- db_driver: DBDriver class
vRP:registerDBDriver(db_driver)
-- prepare a query
--- name: unique name for the query
--- query: SQL string with @params notation
vRP:prepare(name, query)
-- execute a query
--- name: unique name of the query
--- params: map of parameters
--- mode: default is "query"
---- "query": should return rows (list of map of parameter => value), affected
---- "execute": should return affected
---- "scalar": should return a scalar
vRP:query(name, params, mode)
-- shortcut for vRP.query with "execute"
vRP:execute(name, params)
-- shortcut for vRP.query with "scalar"
vRP:scalar(name, params)
-- user data
-- value: binary string
vRP:setUData(user_id,key,value)
vRP:getUData(user_id,key)
-- character data
-- value: binary string
vRP:setCData(character_id,key,value)
vRP:getCData(character_id,key)
-- server data
-- value: binary string
vRP:setSData(key,value,id)
vRP:getSData(key,id)
-- global data
-- value: binary string
vRP:setGData(key,value)
vRP:getGData(key)
-- reason: (optional)
vRP:kick(user, reason)
vRP:save()
(sync) characterLoad(user)
|
called right after the character loading |
(sync) characterUnload(user)
|
called before character unloading |
playerJoin(user)
|
called when a player joins (valid user) |
playerRejoin(user)
|
called when a player re-joins (ex: after a crash) |
playerDelay(user, state)
|
called when the player tunnel delay changes, |
playerSpawn(user, first_spawn)
|
called when the player spawns |
playerDeath(user)
|
called when the player dies |
(sync) playerLeave(user)
|
called before user removal |
save
|
called when vRP performs a save (can be used to sync the save of external extension data) |
Modules
Aptitude
This module adds aptitudes and experience (skill/education).
Extension
User
self.cdata.aptitudes
-- return user aptitudes table
User:getAptitudes()
-- vary experience of an aptitude
User:varyExp(group, aptitude, amount)
-- level up an aptitude
User:levelUp(group, aptitude)
-- level down an aptitude
User:levelDown(group, aptitude)
User:getExp(group, aptitude)
-- set aptitude experience
User:setExp(group, aptitude, amount)
Server
self.cfg
self.exp_step
self.groups
-- define aptitude group
Aptitude:defineGroup(group, title)
-- define aptitude
-- max_exp: -1 => infinite
Aptitude:defineAptitude(group, aptitude, title, init_exp, max_exp)
-- get aptitude definition
Aptitude:getAptitude(group, aptitude)
-- get aptitude group title
-- return string
Aptitude:getGroupTitle(group)
-- convert experience to level
-- return float
Aptitude:expToLevel(exp)
-- convert level to experience
-- return integer
Aptitude:levelToExp(lvl)
Audio
This module handles audio features.
-
a
voip_server
(nodejs) must be launched for each vRP server (by executingmain.js
) -
nodejs
ws
andwrtc
packages are required -
the TCP websocket and UDP webrtc range of ports must be opened by firewalls
-
see
voip_server/config.js
andcfg/audio.lua
The clients will automatically connect/reconnect themselves to the VoIP server (can be started after, shutdown, restarted). |
Extension
Server
self.cfg
self.channels -- map of id => {index, config} (available after the first player connection)
-- register VoIP channel
-- all channels should be registered before any player joins the server
--
-- id: channel name/id (string)
-- config:
--- effects: map of name => true/options
---- spatialization => { max_dist: ..., rolloff: ..., dist_model: ..., ref_dist: ...} (per peer effect)
---- biquad => { frequency: ..., Q: ..., type: ..., detune: ..., gain: ...} see WebAudioAPI BiquadFilter
----- freq = 1700, Q = 3, type = "bandpass" (idea for radio effect)
---- gain => { gain: ... }
Audio:registerVoiceChannel(id, config)
Client
self.voice_channels = {} -- map of channel => map of player => state (0-1)
-- play audio source (once)
--- url: valid audio HTML url (ex: .ogg/.wav/direct ogg-stream url)
--- volume: 0-1
--- x,y,z: position (omit for unspatialized)
--- max_dist (omit for unspatialized)
--- player: (optional) player source id, if passed the spatialized source will be relative to the player (parented)
Audio:playAudioSource(url, volume, x, y, z, max_dist, player)
-- set named audio source (looping)
--- name: source name
--- url: valid audio HTML url (ex: .ogg/.wav/direct ogg-stream url)
--- volume: 0-1
--- x,y,z: position (omit for unspatialized)
--- max_dist (omit for unspatialized)
--- player: (optional) player source id, if passed the spatialized source will be relative to the player (parented)
Audio:setAudioSource(name, url, volume, x, y, z, max_dist, player)
-- remove named audio source
Audio:removeAudioSource(name)
-- VoIP
-- create connection to another player for a specific channel
Audio:connectVoice(channel, player)
-- delete connection to another player for a specific channel
-- player: nil to disconnect from all players
Audio:disconnectVoice(channel, player)
-- enable/disable speaking for a specific channel
--- active: true/false
Audio:setVoiceState(channel, active)
Audio:isSpeaking()
-- TUNNEL
Audio.tunnel.playAudioSource = Audio.playAudioSource
Audio.tunnel.setAudioSource = Audio.setAudioSource
Audio.tunnel.removeAudioSource = Audio.removeAudioSource
Audio.tunnel.connectVoice = Audio.connectVoice
Audio.tunnel.disconnectVoice = Audio.disconnectVoice
Audio.tunnel.setVoiceState = Audio.setVoiceState
speakingChange(speaking)
|
called when speaking state changes |
voiceChannelPlayerSpeakingChange(channel, player, speaking)
|
called when the speaking state of a remote player for a specific channel changes (VoIP) |
voiceChannelTransmittingChange(channel, transmitting)
|
called when the transmitting state of the local player for a specific channel changes (VoIP) |
Base
vRP base module.
Extension
Client
self.id -- user id
self.cid -- character id
self.players -- map of player id, keeps track of connected players (server id)
self.ragdoll -- flag
-- trigger vRP respawn
Base:triggerRespawn()
-- heading: (optional) entity heading
Base:teleport(x,y,z,heading)
-- teleport vehicle when inside one (placed on ground)
-- heading: (optional) entity heading
Base:vehicleTeleport(x,y,z,heading)
-- return x,y,z
Base:getPosition()
-- return false if in exterior, true if inside a building
Base:isInside()
-- return ped speed (based on velocity)
Base:getSpeed()
-- return dx,dy,dz
Base:getCamDirection()
-- return map of player id => distance
Base:getNearestPlayers(radius)
-- return player id or nil
Base:getNearestPlayer(radius)
-- GTA 5 text notification
Base:notify(msg)
-- GTA 5 picture notification
Base:notifyPicture(icon, type, sender, title, text)
-- SCREEN
-- play a screen effect
-- name, see https://wiki.fivem.net/wiki/Screen_Effects
-- duration: in seconds, if -1, will play until stopScreenEffect is called
Base:playScreenEffect(name, duration)
-- stop a screen effect
-- name, see https://wiki.fivem.net/wiki/Screen_Effects
Base:stopScreenEffect(name)
-- ANIM
-- animations dict and names: http://docs.ragepluginhook.net/html/62951c37-a440-478c-b389-c471230ddfc5.htm
-- play animation (new version)
-- upper: true, only upper body, false, full animation
-- seq: list of animations as {dict,anim_name,loops} (loops is the number of loops, default 1) or a task def (properties: task, play_exit)
-- looping: if true, will infinitely loop the first element of the sequence until stopAnim is called
Base:playAnim(upper, seq, looping)
-- stop animation (new version)
-- upper: true, stop the upper animation, false, stop full animations
Base:stopAnim(upper)
-- RAGDOLL
-- set player ragdoll flag (true or false)
Base:setRagdoll(flag)
-- SOUND
-- some lists:
-- pastebin.com/A8Ny8AHZ
-- https://wiki.gtanet.work/index.php?title=FrontEndSoundlist
-- play sound at a specific position
Base:playSpatializedSound(dict,name,x,y,z,range)
-- play sound
Base:playSound(dict,name)
-- TUNNEL
Base.tunnel.triggerRespawn = Base.triggerRespawn
Base.tunnel.teleport = Base.teleport
Base.tunnel.vehicleTeleport = Base.vehicleTeleport
Base.tunnel.getPosition = Base.getPosition
Base.tunnel.isInside = Base.isInside
Base.tunnel.getSpeed = Base.getSpeed
Base.tunnel.getNearestPlayers = Base.getNearestPlayers
Base.tunnel.getNearestPlayer = Base.getNearestPlayer
Base.tunnel.notify = Base.notify
Base.tunnel.notifyPicture = Base.notifyPicture
Base.tunnel.playScreenEffect = Base.playScreenEffect
Base.tunnel.stopScreenEffect = Base.stopScreenEffect
Base.tunnel.playAnim = Base.playAnim
Base.tunnel.stopAnim = Base.stopAnim
Base.tunnel.setRagdoll = Base.setRagdoll
Base.tunnel.playSpatializedSound = Base.playSpatializedSound
Base.tunnel.playSound = Base.playSound
playerTeleport()
|
called when the player is teleported |
Cloak
This module manages character cloaks (uniforms, etc).
Edible
This module defines a generic edible item.
Extension
Server
self.cfg
self.types
self.effects
self.edibles
-- id: identifier (string)
-- action_name: (string)
-- on_consume(user, edible)
Edible:defineType(id, action_name, on_consume)
-- id: identifier (string)
-- on_effect(user, value)
Edible:defineEffect(id, on_effect)
-- id: identifier (string)
-- type: edible type
-- effects: map of effect => value
-- name: (string)
-- description: (html)
-- weight
Edible:defineEdible(id, type, effects, name, description, weight)
Garage
This module manages garages and character vehicles.
Each owned vehicle has a saved state which can be extended with new properties.
Vehicles in FiveM/GTA V are entities which can unload/load/despawn themselves on multiple clients, this module try to handle it this way.
|
Extension
User
self.cdata.vehicles
self.cdata.rent_vehicles
self.vehicle_states
-- get owned vehicles
-- return map of model
User:getVehicles()
-- get vehicle model state table (may be async)
User:getVehicleState(model)
Server
self.cfg
self.models -- map of all garage defined models
-- get vehicle trunk chest id by character id and model
Garage.getVehicleChestId(cid, model)
Client
self.vehicles -- map of vehicle model => veh id (owned vehicles)
self.hash_models -- map of hash => model
-- veh: vehicle game id
-- return owner character id and model or nil if not managed by vRP
Garage:getVehicleInfo(veh)
-- spawn vehicle (will despawn first)
-- will be placed on ground properly
-- one vehicle per model allowed at the same time
--
-- state: (optional) vehicle state (client)
-- position: (optional) {x,y,z}, if not passed the vehicle will be spawned on the player (and will be put inside the vehicle)
-- rotation: (optional) quaternion {x,y,z,w}, if passed with the position, will be applied to the vehicle entity
Garage:spawnVehicle(model, state, position, rotation)
-- return true if despawned
Garage:despawnVehicle(model)
Garage:despawnVehicles()
-- get all game vehicles
-- return list of veh
Garage:getAllVehicles()
-- return map of veh => distance
Garage:getNearestVehicles(radius)
-- return veh
Garage:getNearestVehicle(radius)
Garage:trySpawnOutVehicles()
-- try re-own vehicles
Garage:tryOwnVehicles()
-- cleanup invalid owned vehicles
Garage:cleanupVehicles()
Garage:fixNearestVehicle(radius)
Garage:replaceNearestVehicle(radius)
-- return model or nil
Garage:getNearestOwnedVehicle(radius)
-- return ok,x,y,z
Garage:getAnyOwnedVehiclePosition()
-- return x,y,z or nil
Garage:getOwnedVehiclePosition(model)
Garage:putInOwnedVehicle(model)
-- eject the ped from the vehicle
Garage:ejectVehicle()
Garage:isInVehicle()
-- return model or nil if not in owned vehicle
Garage:getInOwnedVehicleModel()
-- VEHICLE STATE
Garage:getVehicleCustomization(veh)
-- partial update per property
Garage:setVehicleCustomization(veh, custom)
Garage:getVehicleState(veh)
-- partial update per property
Garage:setVehicleState(veh, state)
-- VEHICLE COMMANDS
Garage:vc_openDoor(model, door_index)
Garage:vc_closeDoor(model, door_index)
Garage:vc_detachTrailer(model)
Garage:vc_detachTowTruck(model)
Garage:vc_detachCargobob(model)
Garage:vc_toggleEngine(model)
-- return true if locked, false if unlocked
Garage:vc_toggleLock(model)
-- TUNNEL
Garage.tunnel.spawnVehicle = Garage.spawnVehicle
Garage.tunnel.despawnVehicle = Garage.despawnVehicle
Garage.tunnel.despawnVehicles = Garage.despawnVehicles
Garage.tunnel.fixNearestVehicle = Garage.fixNearestVehicle
Garage.tunnel.replaceNearestVehicle = Garage.replaceNearestVehicle
Garage.tunnel.getNearestOwnedVehicle = Garage.getNearestOwnedVehicle
Garage.tunnel.getAnyOwnedVehiclePosition = Garage.getAnyOwnedVehiclePosition
Garage.tunnel.getOwnedVehiclePosition = Garage.getOwnedVehiclePosition
Garage.tunnel.putInOwnedVehicle = Garage.putInOwnedVehicle
Garage.tunnel.getInOwnedVehicleModel = Garage.getInOwnedVehicleModel
Garage.tunnel.trySpawnOutVehicles = Garage.trySpawnOutVehicles
Garage.tunnel.cleanupVehicles = Garage.cleanupVehicles
Garage.tunnel.tryOwnVehicles = Garage.tryOwnVehicles
Garage.tunnel.ejectVehicle = Garage.ejectVehicle
Garage.tunnel.isInVehicle = Garage.isInVehicle
Garage.tunnel.vc_openDoor = Garage.vc_openDoor
Garage.tunnel.vc_closeDoor = Garage.vc_closeDoor
Garage.tunnel.vc_detachTrailer = Garage.vc_detachTrailer
Garage.tunnel.vc_detachTowTruck = Garage.vc_detachTowTruck
Garage.tunnel.vc_detachCargobob = Garage.vc_detachCargobob
Garage.tunnel.vc_toggleEngine = Garage.vc_toggleEngine
Garage.tunnel.vc_toggleLock = Garage.vc_toggleLock
garageVehicleSpawn(model)
|
called when a vehicle is spawned |
garageVehicleDespawn(model)
|
called when a vehicle is despawned |
Group
This module adds groups and permissions.
Extension
User
self.cdata.groups
-- return map of groups
User:getGroups()
User:hasGroup(name)
User:addGroup(name)
User:removeGroup(name)
-- get user group by type
-- return group name or nil
User:getGroupByType(gtype)
-- check if the user has a specific permission
User:hasPermission(perm)
-- check if the user has a specific list of permissions (all of them)
User:hasPermissions(perms)
Server
self.cfg
-- return users list
Group:getUsersByGroup(name)
-- return users list
Group:getUsersByPermission(perm)
-- return title or nil
Group:getGroupTitle(group_name)
-- register a special permission function
-- name: name of the permission -> "!name.[...]"
-- callback(user, params)
--- params: params (strings) of the permissions, ex "!name.param1.param2" -> ["name", "param1", "param2"]
--- should return true or false/nil
Group:registerPermissionFunction(name, callback)
playerJoinGroup(user, name, gtype)
|
called when a player joins a group |
playerLeaveGroup(user, name, gtype)
|
called when a player leaves a group |
GUI
This module adds GUI features.
If GUI customization is needed, it’s possible to use It’s even possible to load a custom client-side
design.css file like this:
|
Extension
Menu
self.user
self.name
self.data
self.title
self.options
self.css
self.closed
-- dispatcher events:
--- close(menu)
--- remove(menu)
--- select(menu, id)
Menu:listen(name, callback)
-- add option
-- title: option title (html)
-- action(menu, value, mod, index): (optional) select callback
--- value: option value
--- mod: action modulation
---- -1: left
---- 0: valid
---- 1: right
-- description: (optional) option description (html)
--- callback(menu, value): should return a string or nil
-- value: (optional) option value, can be anything, option index by default
-- index: (optional) by default the option is added at the end, but an index can be used to insert the option
Menu:addOption(title, action, description, value, index)
-- update menu option
-- title: (optional) as Menu:addOption
-- description: (optional) as Menu:addOption
-- will trigger client update if current menu
Menu:updateOption(id, title, description)
User
self.menu_stack -- stack of menus
self.request_ids
self.requests
-- return current menu or nil
User:getMenu()
-- open menu (build and open menu)
-- data: (optional) menu build data
-- return menu
User:openMenu(name, data)
-- close menu
-- menu: (optional) menu to close, if nil, will close the current menu
User:closeMenu(menu)
-- close and rebuild current menu (no remove)
-- menu is rebuilt, listeners are kept
User:actualizeMenu()
-- close all menus
User:closeMenus()
-- prompt textual (and multiline) information from player
-- return entered text
User:prompt(title, default_text)
-- REQUEST
-- ask something to a player with a limited amount of time to answer (yes|no request)
-- time: request duration in seconds
-- return true (yes) or false (no)
User:request(text, time)
Server
self.cfg
self.menu_builders -- map of name => callbacks list
-- register a menu builder function
-- name: menu type name
-- builder(menu): callback to modify the menu
GUI:registerMenuBuilder(name, builder)
-- TUNNEL
-- open the general player menu
GUI.tunnel:openMainMenu()
-- can be used to implement a custom menu
GUI.tunnel:closeMenu()
GUI.tunnel:triggerMenuOption(id, mod)
GUI.tunnel:triggerMenuSelect(id)
Client
self.paused
self.menu_data
-- CONTROLS/GUI
GUI:isPaused()
-- get native GUI coordinates based on UI description
-- x_align, y_align: integers, see https://runtime.fivem.net/doc/natives/#_0xB8A850F20A067EB6
-- x, y: floats, UI defined coordinates
GUI:getNativeCoords(x_align, y_align, x, y)
-- get minimap rect in pixels
-- return x, y, w, h
GUI:getMinimapRect()
-- hide/show GUI
GUI:setVisible(flag)
-- MENU
GUI:isMenuOpen()
-- ANNOUNCE
-- add an announce to the queue
-- background: image url (800x150)
-- content: announce html content
GUI:announce(background,content)
-- PROGRESS BAR
-- create/update a progress bar
-- anchor:
--- "minimap"
--- "center"
--- "botright"
-- r,g,b: RGB 256 color
-- value: 0-1
GUI:setProgressBar(name,anchor,text,r,g,b,value)
-- set progress bar value 0-1
GUI:setProgressBarValue(name,value)
-- set progress bar text
GUI:setProgressBarText(name,text)
-- remove a progress bar
GUI:removeProgressBar(name)
-- DIV
-- set a div
-- css: plain global css, the div class is "div_name"
-- content: html content of the div
GUI:setDiv(name,css,content)
-- set the div css
GUI:setDivCss(name,css)
-- set the div content
GUI:setDivContent(name,content)
-- execute js for the div
-- js: code, "this" is the div
GUI:divExecuteJS(name,js)
-- remove the div
GUI:removeDiv(name)
-- TUNNEL
GUI.tunnel.announce = GUI.announce
GUI.tunnel.setProgressBar = GUI.setProgressBar
GUI.tunnel.setProgressBarValue = GUI.setProgressBarValue
GUI.tunnel.setProgressBarText = GUI.setProgressBarText
GUI.tunnel.removeProgressBar = GUI.removeProgressBar
GUI.tunnel.setDiv = GUI.setDiv
GUI.tunnel.setDivCss = GUI.setDivCss
GUI.tunnel.setDivContent = GUI.setDivContent
GUI.tunnel.divExecuteJS = GUI.divExecuteJS
GUI.tunnel.removeDiv = GUI.removeDiv
NUIready()
|
called when the vRP NUI is ready |
pauseChange(paused)
|
called when the game pause state changes |
menuOpen(menudata)
|
called when the menu is opened (can be used to implement a custom menu) |
menuClose()
|
called when the menu is closed (can be used to implement a custom menu) |
menuSetSelectEvent(select_event)
|
called when the select event flag is set (if true, the menu must trigger the server-side option select event) (can be used to implement a custom menu) |
menuOptionUpdate(index, title, description)
|
called when a menu option is updated (can be used to implement a custom menu) |
HiddenTransformer
This module adds hidden/randomly placed transformers and an informer to find them.
Home
This module adds a home system.
Extension
Component
self.slot
self.id
self.index
self.cfg
self.x
self.y
self.z
-- called when the component is loaded for a specific slot
Component:load()
-- called when the component is unloaded from a specific slot
Component:unload()
-- called when a player enters the slot
Component:enter(user)
-- called when a player leaves the slot
Component:leave(user)
Slot
self.type
self.id
self.owner_id -- character id
self.home
self.number
self.users -- map of users
self.components -- map of index => component
Slot:isEmpty()
User
self.home_slot
self.address
-- access a home by address
-- return true on success
User:accessHome(home, number)
User:leaveHome()
-- check if inside a home
User:inHome()
Server
self.cfg
self.components
self.slots -- map of type => map of slot id => slot instance
-- address access (online and offline characters)
-- return address or nil
Home:getAddress(cid)
-- return character id or nil
Home:getByAddress(home,number)
-- find a free address number to buy
-- return number or nil if no numbers availables
Home:findFreeNumber(home,max)
-- register home component
-- id: unique component identifier (string)
-- component: Home.Component derived class
Home:registerComponent(component)
-- SLOTS
-- get slot instance
-- return slot or nil
Home:getSlot(stype, sid)
-- get slot instance by address
-- return slot or nil
Home:getSlotByAddress(home, number)
-- return sid or nil
Home:findFreeSlot(stype)
characterAddressUpdate(user)
|
called when the address is updated (load, buy, sell…) |
home_components
This module adds some home components.
Home component
chest
A home chest to store items. The chest is bound to the character, not to the home or the component.
weight |
(optional) chest max weight, 200 by default |
Identity
This module adds an identity system.
Extension
Server
self.cfg
-- (ex: DDDLLL, D => digit, L => letter)
Identity.generateStringNumber(format)
-- identity access (online and offline characters)
-- return identity or nil
Identity:getIdentity(cid)
-- return character_id or nil
Identity:getByRegistration(registration)
-- return character_id or nil
Identity:getByPhone(phone)
-- return a unique registration number
Identity:generateRegistrationNumber()
-- return a unique phone number
Identity:generatePhoneNumber()
characterIdentityUpdate(user)
|
called when the identity is updated (load, change…) |
Inventory
This module adds an inventory/item system.
Extension
User
self.cdata.inventory
-- return map of fullid => amount
User:getInventory()
-- try to give an item
-- dry: if passed/true, will not affect
-- return true on success or false
User:tryGiveItem(fullid,amount,dry,no_notify)
-- try to take an item from inventory
-- dry: if passed/true, will not affect
-- return true on success or false
User:tryTakeItem(fullid,amount,dry,no_notify)
-- get item amount in the inventory
User:getItemAmount(fullid)
-- return inventory total weight
User:getInventoryWeight()
-- return maximum weight of inventory
User:getInventoryMaxWeight()
User:clearInventory()
-- open a chest by identifier (GData)
-- cb_close(id): called when the chest is closed (optional)
-- cb_in(chest_id, fullid, amount): called when an item is added (optional)
-- cb_out(chest_id, fullid, amount): called when an item is taken (optional)
-- return chest menu or nil
User:openChest(id, max_weight, cb_close, cb_in, cb_out)
Server
self.cfg
self.items -- item definitions
self.computed_items -- computed item definitions
self.chests -- loaded chests
-- define an inventory item (parametric or plain text data)
-- id: unique item identifier (string, no "." or "|")
-- name: display name, value or genfunction(args)
-- description: value or genfunction(args) (html)
-- menu_builder: (optional) genfunction(args, menu)
-- weight: (optional) value or genfunction(args)
--
-- genfunction are functions returning a correct value as: function(args, ...)
-- where args is a list of {base_idname,args...}
Inventory:defineItem(id,name,description,menu_builder,weight)
-- compute item definition (cached)
-- return computed item or nil
-- computed item {}
--- name
--- description
--- weight
--- menu_builder: can be nil
--- args: parametric args
Inventory:computeItem(fullid)
-- compute weight of a list of items (in inventory/chest format)
Inventory:computeItemsWeight(items)
-- load global chest
-- id: identifier (string)
-- return chest (as inventory, map of fullid => amount)
Inventory:loadChest(id)
-- unload global chest
-- id: identifier (string)
Inventory:unloadChest(id)
Menu
Login
This is the base login module with whitelist and ban features.
Extension
Server
self.cfg
-- return (end_timestamp, reason) if banned or nil
Login:checkBanned(user_id)
-- end_timestamp: POSIX timestamp (0: not banned, 2^32-1: banned "forever")
-- reason: (optional)
Login:setBanned(user_id, end_timestamp, reason)
Login:isWhitelisted(user_id)
Login:setWhitelisted(user_id, whitelisted)
-- duration: in seconds (-1: ban "forever")
-- reason: (optional)
Login:ban(user, duration, reason)
Map
This module adds map features.
Extension
Map.Entity
Client-side map entity.
self.id
self.cfg
-- called when the entity is loaded
Entity:load()
-- called when the entity is unloaded
Entity:unload()
-- called to check if the entity is active
-- px, py, pz: player position
-- should return true if active
Entity:active(px, py, pz)
-- called at each render frame if the entity is active
-- time: seconds since last frame
Entity:frame(time)
Entity.command
table can be created to define entity commands, the same way as for the Extension.proxy
table.
User
self.map_areas
-- create/update a player area
-- cb_enter(user, name): (optional) called when entering the area
-- cb_leave(user, name): (optional) called when leaving the area
User:setArea(name,x,y,z,radius,height,cb_enter,cb_leave)
User:removeArea(name)
User:inArea(name)
Client
self.entities -- map of id (number or name) => entity
self.def_entities -- defined entities
self.frame_entities -- active entities for the next frames
self.areas
-- ent: Map.Entity class
Map:registerEntity(ent)
-- add entity
-- ent: registered entity name
-- cfg: entity config
-- return id (number) or nil on failure
Map:addEntity(ent, cfg)
-- id: number or string
Map:removeEntity(id)
-- id: number (update added entity) or string (create/update named entity)
-- ent: registered entity name
-- cfg: entity config
Map:setEntity(id, ent, cfg)
-- entity command
-- id: number or string
-- command: command name
-- ...: arguments
Map:commandEntity(id, command, ...)
-- GPS
-- set the GPS destination marker coordinates
Map:setGPS(x,y)
-- DOOR
-- set the closest door state
-- doordef: .model or .modelhash
-- locked: boolean
-- doorswing: -1 to 1
Map:setStateOfClosestDoor(doordef, locked, doorswing)
-- TUNNEL
Map.tunnel.addEntity = Map.addEntity
Map.tunnel.setEntity = Map.setEntity
Map.tunnel.removeEntity = Map.removeEntity
Map.tunnel.commandEntity = Map.commandEntity
Map.tunnel.setGPS = Map.setGPS
Map.tunnel.setStateOfClosestDoor = Map.setStateOfClosestDoor
Map entity
Map.PosEntity
Specialized entity with a position and default active behavior by distance.
pos |
|
active_distance |
(optional) maximum distance for the entity to be active, default is 250 |
PoI
Point of Interest generic map entity (blip/marker).
Inherits from Map.PosEntity
.
blip_id, blip_color |
(optional) blip id/color (ex: https://wiki.gtanet.work/index.php?title=Blips) |
blip_scale |
(optional) float |
blip_flashes |
(optional) mode number |
title |
(optional) title (used for the blip) |
marker_id |
(optional) marker id (ex: https://wiki.gtanet.work/index.php?title=Marker) |
scale |
(optional) |
color |
(optional) |
height |
(optional) marker height |
rotate_speed |
(optional) number of z-axis rotations per second |
setBlipRoute()
|
set player blip route to |
PlayerMark
Mark on player ped.
player |
player server id |
blip_id, blip_color |
(optional) blip id/color (ex: https://wiki.gtanet.work/index.php?title=Blips) |
blip_scale |
(optional) float |
blip_flashes |
(optional) mode number |
title |
(optional) title (used for the blip) |
Mission
This module adds a simple mission system.
Extension
User
self.mission
self.mission_step
-- start a mission for a player
--- mission:
---- name: mission name
---- steps: ordered list of
----- text: (html)
----- position: {x,y,z}
----- radius: (optional) area radius (affect default PoI)
----- height: (optional) area height (affect default PoI)
----- onenter: see Map.User:setArea
----- onleave: (optional) see Map.User:setArea
----- map_entity: (optional) a simple PoI by default
User:startMission(mission)
-- end the current player mission step
User:nextMissionStep()
-- stop the player mission
User:stopMission()
-- check if the player has a mission
User:hasMission()
Server
self.cfg
playerMissionStart(user)
|
called when the player start a mission (the mission data is attached to the player at the beginning of the call) |
playerMissionStep(user)
|
called when the player start a new mission step |
playerMissionStop(user)
|
called when the player stop the mission (the mission data is still attached to the player at the beginning of the call) |
Money
This module adds money (wallet and bank).
Extension
User
self.cdata.wallet
self.cdata.bank
-- get wallet amount
User:getWallet()
-- get bank amount
User:getBank()
-- set wallet amount
User:setWallet(amount)
-- set bank amount
User:setBank(amount)
-- give money to bank
User:giveBank(amount)
-- give money to wallet
User:giveWallet(amount)
-- try a payment (with wallet)
-- dry: if passed/true, will not affect
-- return true if debited or false
User:tryPayment(amount, dry)
-- try a withdraw (from bank)
-- dry: if passed/true, will not affect
-- return true if withdrawn or false
User:tryWithdraw(amount, dry)
-- try a deposit
-- dry: if passed/true, will not affect
-- return true if deposited or false
User:tryDeposit(amount, dry)
-- try full payment (wallet + bank to complete payment)
-- dry: if passed/true, will not affect
-- return true if debited or false
User:tryFullPayment(amount, dry)
Phone
This module adds a phone.
Extension
User
self.phone_sms
self.cdata.phone_directory
self.phone_call -- source of correspondent if in phone call
-- send an sms to a phone number
-- return true on success
User:sendSMS(phone, msg)
-- add sms to phone
User:addSMS(phone, msg)
-- get directory name by number for a specific user
User:getPhoneDirectoryName(phone)
User:phoneHangUp()
-- call a phone number
-- return true if the communication is established
User:phoneCall(phone)
-- send smspos to a phone number
-- return true on success
User:sendSMSPos(phone, x,y,z)
PlayerState
This module handles the player/character state (customization, weapons, health, armour…).
Extension
Server
self.cfg
playerStateLoaded(user)
|
called when the player state loading is finished |
playerStateUpdate(user, state)
|
called when the player state is updated ( |
Client
self.state_ready
-- WEAPONS
-- get player weapons
-- return map of name => {.ammo}
PlayerState:getWeapons()
-- replace weapons (combination of getWeapons and giveWeapons)
-- weapons: map of name => {.ammo}
--- ammo: (optional)
-- return previous weapons
PlayerState:replaceWeapons(weapons)
-- weapons: map of name => {.ammo}
--- ammo: (optional)
PlayerState:giveWeapons(weapons, clear_before)
-- set player armour (0-100)
PlayerState:setArmour(amount)
PlayerState:getArmour()
-- amount: 100-200 ?
PlayerState:setHealth(amount)
PlayerState:getHealth()
-- PLAYER CUSTOMIZATION
-- get number of drawables for a specific part
PlayerState:getDrawables(part)
-- get number of textures for a specific part and drawable
PlayerState:getDrawableTextures(part,drawable)
-- get player skin customization
-- return custom parts
PlayerState:getCustomization()
-- set partial customization (only what is set is changed)
-- custom: indexed customization parts ("foo:arg1:arg2...")
--- "modelhash": number, model hash
--- or "model": string, model name
--- "drawable:<index>": {drawable,texture,palette} ped components
--- "prop:<index>": {prop_index, prop_texture}
--- "hair_color": {primary, secondary}
--- "overlay:<index>": {overlay_index, primary color, secondary color, opacity}
PlayerState:setCustomization(custom)
-- TUNNEL
PlayerState.tunnel.getWeapons = PlayerState.getWeapons
PlayerState.tunnel.replaceWeapons = PlayerState.replaceWeapons
PlayerState.tunnel.giveWeapons = PlayerState.giveWeapons
PlayerState.tunnel.setArmour = PlayerState.setArmour
PlayerState.tunnel.getArmour = PlayerState.getArmour
PlayerState.tunnel.setHealth = PlayerState.setHealth
PlayerState.tunnel.getHealth = PlayerState.getHealth
PlayerState.tunnel.getDrawables = PlayerState.getDrawables
PlayerState.tunnel.getDrawableTextures = PlayerState.getDrawableTextures
PlayerState.tunnel.getCustomization = PlayerState.getCustomization
PlayerState.tunnel.setCustomization = PlayerState.setCustomization
(sync) playerModelSave
|
called before the player model is changed (can be used to save some native player data) |
(sync) playerModelRestore
|
called after the player model is changed (can be used to restore some native player data) |
Police
This module adds some police features.
Extension
User
self.police_records -- list of strings
-- insert a police record (will do a save)
--- record: text for one line (html)
User:insertPoliceRecord(record)
User:savePoliceRecords()
Client
self.handcuffed -- flag
self.cop -- flag
self.wanted_level
self.current_jail
self.follow_player
-- set player as cop (true or false)
Police:setCop(flag)
-- HANDCUFF
Police:toggleHandcuff()
Police:setHandcuffed(flag)
Police:isHandcuffed()
Police:putInNearestVehicleAsPassenger(radius)
-- FOLLOW
-- follow another player
-- player: nil to disable
Police:followPlayer(player)
-- return player or nil if not following anyone
Police:getFollowedPlayer()
-- JAIL
-- jail the player in a no-top no-bottom cylinder
Police:jail(x,y,z,radius)
-- unjail the player
Police:unjail()
Police:isJailed()
-- WANTED
Police:applyWantedLevel(new_wanted)
-- TUNNEL
Police.tunnel.setCop = Police.setCop
Police.tunnel.toggleHandcuff = Police.toggleHandcuff
Police.tunnel.setHandcuffed = Police.setHandcuffed
Police.tunnel.isHandcuffed = Police.isHandcuffed
Police.tunnel.putInNearestVehicleAsPassenger = Police.putInNearestVehicleAsPassenger
Police.tunnel.followPlayer = Police.followPlayer
Police.tunnel.getFollowedPlayer = Police.getFollowedPlayer
Police.tunnel.jail = Police.jail
Police.tunnel.unjail = Police.unjail
Police.tunnel.isJailed = Police.isJailed
Police.tunnel.applyWantedLevel = Police.applyWantedLevel
Survival
This module adds survival features.
-
food
-
water
Extension
User
self.cdata.vitals -- map of name => value
-- return vital value (0-1) or nil
User:getVital(name)
-- set vital
-- value: 0-1
User:setVital(name, value)
User:varyVital(name, value)
Server
self.cfg
self.vitals -- registered vitals, map of name => {default_value}
playerVitalChange(user, name)
|
called when a player vital value changes |
playerVitalOverflow(user, name, overflow)
|
called when a player vital overflows (overflow can be negative or positive) |
Client
self.in_coma -- flag
self.coma_left -- seconds
Survival:varyHealth(variation)
Survival:setFriendlyFire(flag)
Survival:setPolice(flag)
-- COMA SYSTEM
Survival:isInComa()
-- kill the player if in coma
Survival:killComa()
-- TUNNEL
Survival.tunnel.isInComa = Survival.isInComa
Survival.tunnel.killComa = Survival.killComa
Survival.tunnel.setFriendlyFire = Survival.setFriendlyFire
Survival.tunnel.setPolice = Survival.setPolice
Survival.tunnel.varyHealth = Survival.varyHealth
Transformer
This module adds transformers.
-
it’s a generic system to transform (generate, process, convert) items/money/etc to items/money/etc in a specific area
-
each transformer can take things to generate other things, using a unit of work
-
units are regenerated periodically at a specific rate
-
reagents ⇒ products (ex: reagents can be nothing to create an harvest transformer)
Extension
Transformer
self.id
self.cfg
self.units
self.users -- map of user => recipe name
Transformer:unbindUser(user)
Transformer:bindUser(user, recipe_name)
Transformer:unbindAll()
Server
self.cfg
self.transformers -- map of id => transformer
self.processors -- registered processors, map of id => {on_display, on_check, on_process}
-- register a transformer processor
-- on_display(user, reagents, products): should return r_info, p_info (two html strings to display info about the reagents and products)
-- on_check(user, reagents, products): should return true if the processing can occur
-- on_process(user, reagents, products): should process the transformation
-- for the three callbacks:
--- reagents: reagents data, can be nil
--- products: products data, can be nil
Transformer:registerProcessor(id, on_display, on_check, on_process)
-- add a transformer
-- id: transformer identitifer (string)
-- cfg: transformer config
--- title
--- color {r,g,b} (255)
--- max_units
--- units_per_minute
--- pos {x,y,z}
--- radius,height (area properties)
--- permissions: (optional)
--- recipes: map of recipe name => recipe {}
---- description (html)
---- reagents: map of processor id => data, see modules transformer processors
---- products: map of processor id => data, see modules transformer processors
---- permissions: (optional) recipe permissions
---- onstart(transformer, user, recipe_name): (optional) called when the recipe starts
---- onstep(transformer, user, recipe_name): (optional) called at each recipe step
---- onstop(transformer, user, recipe_name): (optional) called when the recipe stops
Transformer:set(id, cfg)
-- remove a transformer
Transformer:remove(id)